Put sections to segments according to linker scripts if available. Rework the code of TargetLayout::assignSectionsToSegments so it operates on the given list of segments, which can be either read from linker scripts or constructed as before. Handle NONE segments defined in linker scripts by putting corresponding sections to PT_NULL segment. Consider flags set for segments through linker scripts. Differential Revision: http://reviews.llvm.org/D10918 llvm-svn: 243002
2850 lines
75 KiB
C++
2850 lines
75 KiB
C++
//===- ReaderWriter/LinkerScript.cpp --------------------------------------===//
|
|
//
|
|
// The LLVM Linker
|
|
//
|
|
// This file is distributed under the University of Illinois Open Source
|
|
// License. See LICENSE.TXT for details.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
///
|
|
/// \file
|
|
/// \brief Linker script parser.
|
|
///
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "lld/ReaderWriter/LinkerScript.h"
|
|
|
|
#include "llvm/ADT/APInt.h"
|
|
#include "llvm/ADT/StringExtras.h"
|
|
#include "llvm/Support/Errc.h"
|
|
#include "llvm/Support/ELF.h"
|
|
|
|
namespace lld {
|
|
namespace script {
|
|
void Token::dump(raw_ostream &os) const {
|
|
switch (_kind) {
|
|
#define CASE(name) \
|
|
case Token::name: \
|
|
os << #name ": "; \
|
|
break;
|
|
CASE(unknown)
|
|
CASE(eof)
|
|
CASE(exclaim)
|
|
CASE(exclaimequal)
|
|
CASE(amp)
|
|
CASE(ampequal)
|
|
CASE(l_paren)
|
|
CASE(r_paren)
|
|
CASE(star)
|
|
CASE(starequal)
|
|
CASE(plus)
|
|
CASE(plusequal)
|
|
CASE(comma)
|
|
CASE(minus)
|
|
CASE(minusequal)
|
|
CASE(slash)
|
|
CASE(slashequal)
|
|
CASE(number)
|
|
CASE(colon)
|
|
CASE(semicolon)
|
|
CASE(less)
|
|
CASE(lessequal)
|
|
CASE(lessless)
|
|
CASE(lesslessequal)
|
|
CASE(equal)
|
|
CASE(equalequal)
|
|
CASE(greater)
|
|
CASE(greaterequal)
|
|
CASE(greatergreater)
|
|
CASE(greatergreaterequal)
|
|
CASE(question)
|
|
CASE(identifier)
|
|
CASE(libname)
|
|
CASE(kw_align)
|
|
CASE(kw_align_with_input)
|
|
CASE(kw_as_needed)
|
|
CASE(kw_at)
|
|
CASE(kw_discard)
|
|
CASE(kw_entry)
|
|
CASE(kw_exclude_file)
|
|
CASE(kw_extern)
|
|
CASE(kw_fill)
|
|
CASE(kw_group)
|
|
CASE(kw_hidden)
|
|
CASE(kw_input)
|
|
CASE(kw_keep)
|
|
CASE(kw_length)
|
|
CASE(kw_memory)
|
|
CASE(kw_origin)
|
|
CASE(kw_phdrs)
|
|
CASE(kw_provide)
|
|
CASE(kw_provide_hidden)
|
|
CASE(kw_only_if_ro)
|
|
CASE(kw_only_if_rw)
|
|
CASE(kw_output)
|
|
CASE(kw_output_arch)
|
|
CASE(kw_output_format)
|
|
CASE(kw_overlay)
|
|
CASE(kw_search_dir)
|
|
CASE(kw_sections)
|
|
CASE(kw_sort_by_alignment)
|
|
CASE(kw_sort_by_init_priority)
|
|
CASE(kw_sort_by_name)
|
|
CASE(kw_sort_none)
|
|
CASE(kw_subalign)
|
|
CASE(l_brace)
|
|
CASE(pipe)
|
|
CASE(pipeequal)
|
|
CASE(r_brace)
|
|
CASE(tilde)
|
|
#undef CASE
|
|
}
|
|
os << _range << "\n";
|
|
}
|
|
|
|
static llvm::ErrorOr<uint64_t> parseDecimal(StringRef str) {
|
|
uint64_t res = 0;
|
|
for (auto &c : str) {
|
|
res *= 10;
|
|
if (c < '0' || c > '9')
|
|
return llvm::ErrorOr<uint64_t>(make_error_code(llvm::errc::io_error));
|
|
res += c - '0';
|
|
}
|
|
return res;
|
|
}
|
|
|
|
static llvm::ErrorOr<uint64_t> parseOctal(StringRef str) {
|
|
uint64_t res = 0;
|
|
for (auto &c : str) {
|
|
res <<= 3;
|
|
if (c < '0' || c > '7')
|
|
return llvm::ErrorOr<uint64_t>(make_error_code(llvm::errc::io_error));
|
|
res += c - '0';
|
|
}
|
|
return res;
|
|
}
|
|
|
|
static llvm::ErrorOr<uint64_t> parseBinary(StringRef str) {
|
|
uint64_t res = 0;
|
|
for (auto &c : str) {
|
|
res <<= 1;
|
|
if (c != '0' && c != '1')
|
|
return llvm::ErrorOr<uint64_t>(make_error_code(llvm::errc::io_error));
|
|
res += c - '0';
|
|
}
|
|
return res;
|
|
}
|
|
|
|
static llvm::ErrorOr<uint64_t> parseHex(StringRef str) {
|
|
uint64_t res = 0;
|
|
for (auto &c : str) {
|
|
res <<= 4;
|
|
if (c >= '0' && c <= '9')
|
|
res += c - '0';
|
|
else if (c >= 'a' && c <= 'f')
|
|
res += c - 'a' + 10;
|
|
else if (c >= 'A' && c <= 'F')
|
|
res += c - 'A' + 10;
|
|
else
|
|
return llvm::ErrorOr<uint64_t>(make_error_code(llvm::errc::io_error));
|
|
}
|
|
return res;
|
|
}
|
|
|
|
static bool parseHexToByteStream(StringRef str, std::string &buf) {
|
|
unsigned char byte = 0;
|
|
bool dumpByte = str.size() % 2;
|
|
for (auto &c : str) {
|
|
byte <<= 4;
|
|
if (c >= '0' && c <= '9')
|
|
byte += c - '0';
|
|
else if (c >= 'a' && c <= 'f')
|
|
byte += c - 'a' + 10;
|
|
else if (c >= 'A' && c <= 'F')
|
|
byte += c - 'A' + 10;
|
|
else
|
|
return false;
|
|
if (!dumpByte) {
|
|
dumpByte = true;
|
|
continue;
|
|
}
|
|
buf.push_back(byte);
|
|
byte = 0;
|
|
dumpByte = false;
|
|
}
|
|
return !dumpByte;
|
|
}
|
|
|
|
static void dumpByteStream(raw_ostream &os, StringRef stream) {
|
|
os << "0x";
|
|
for (auto &c : stream) {
|
|
unsigned char firstNibble = c >> 4 & 0xF;
|
|
if (firstNibble > 9)
|
|
os << (char) ('A' + firstNibble - 10);
|
|
else
|
|
os << (char) ('0' + firstNibble);
|
|
unsigned char secondNibble = c & 0xF;
|
|
if (secondNibble > 9)
|
|
os << (char) ('A' + secondNibble - 10);
|
|
else
|
|
os << (char) ('0' + secondNibble);
|
|
}
|
|
}
|
|
|
|
static llvm::ErrorOr<uint64_t> parseNum(StringRef str) {
|
|
unsigned multiplier = 1;
|
|
enum NumKind { decimal, hex, octal, binary };
|
|
NumKind kind = llvm::StringSwitch<NumKind>(str)
|
|
.StartsWith("0x", hex)
|
|
.StartsWith("0X", hex)
|
|
.StartsWith("0", octal)
|
|
.Default(decimal);
|
|
|
|
// Parse scale
|
|
if (str.endswith("K")) {
|
|
multiplier = 1 << 10;
|
|
str = str.drop_back();
|
|
} else if (str.endswith("M")) {
|
|
multiplier = 1 << 20;
|
|
str = str.drop_back();
|
|
}
|
|
|
|
// Parse type
|
|
if (str.endswith_lower("o")) {
|
|
kind = octal;
|
|
str = str.drop_back();
|
|
} else if (str.endswith_lower("h")) {
|
|
kind = hex;
|
|
str = str.drop_back();
|
|
} else if (str.endswith_lower("d")) {
|
|
kind = decimal;
|
|
str = str.drop_back();
|
|
} else if (str.endswith_lower("b")) {
|
|
kind = binary;
|
|
str = str.drop_back();
|
|
}
|
|
|
|
llvm::ErrorOr<uint64_t> res(0);
|
|
switch (kind) {
|
|
case hex:
|
|
if (str.startswith_lower("0x"))
|
|
str = str.drop_front(2);
|
|
res = parseHex(str);
|
|
break;
|
|
case octal:
|
|
res = parseOctal(str);
|
|
break;
|
|
case decimal:
|
|
res = parseDecimal(str);
|
|
break;
|
|
case binary:
|
|
res = parseBinary(str);
|
|
break;
|
|
}
|
|
if (res.getError())
|
|
return res;
|
|
|
|
*res = *res * multiplier;
|
|
return res;
|
|
}
|
|
|
|
bool Lexer::canStartNumber(char c) const {
|
|
return '0' <= c && c <= '9';
|
|
}
|
|
|
|
bool Lexer::canContinueNumber(char c) const {
|
|
// [xX] = hex marker, [hHoO] = type suffix, [MK] = scale suffix.
|
|
return strchr("0123456789ABCDEFabcdefxXhHoOMK", c);
|
|
}
|
|
|
|
bool Lexer::canStartName(char c) const {
|
|
return strchr(
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_.$/\\*", c);
|
|
}
|
|
|
|
bool Lexer::canContinueName(char c) const {
|
|
return strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
|
"0123456789_.$/\\~=+[]*?-:", c);
|
|
}
|
|
|
|
/// Helper function to split a StringRef in two at the nth character.
|
|
/// The StringRef s is updated, while the function returns the n first
|
|
/// characters.
|
|
static StringRef drop(StringRef &s, int n) {
|
|
StringRef res = s.substr(0, n);
|
|
s = s.drop_front(n);
|
|
return res;
|
|
}
|
|
|
|
void Lexer::lex(Token &tok) {
|
|
skipWhitespace();
|
|
if (_buffer.empty()) {
|
|
tok = Token(_buffer, Token::eof);
|
|
return;
|
|
}
|
|
switch (_buffer[0]) {
|
|
case 0:
|
|
tok = Token(drop(_buffer, 1), Token::eof);
|
|
return;
|
|
case '(':
|
|
tok = Token(drop(_buffer, 1), Token::l_paren);
|
|
return;
|
|
case ')':
|
|
tok = Token(drop(_buffer, 1), Token::r_paren);
|
|
return;
|
|
case '{':
|
|
tok = Token(drop(_buffer, 1), Token::l_brace);
|
|
return;
|
|
case '}':
|
|
tok = Token(drop(_buffer, 1), Token::r_brace);
|
|
return;
|
|
case '=':
|
|
if (_buffer.startswith("==")) {
|
|
tok = Token(drop(_buffer, 2), Token::equalequal);
|
|
return;
|
|
}
|
|
tok = Token(drop(_buffer, 1), Token::equal);
|
|
return;
|
|
case '!':
|
|
if (_buffer.startswith("!=")) {
|
|
tok = Token(drop(_buffer, 2), Token::exclaimequal);
|
|
return;
|
|
}
|
|
tok = Token(drop(_buffer, 1), Token::exclaim);
|
|
return;
|
|
case ',':
|
|
tok = Token(drop(_buffer, 1), Token::comma);
|
|
return;
|
|
case ';':
|
|
tok = Token(drop(_buffer, 1), Token::semicolon);
|
|
return;
|
|
case ':':
|
|
tok = Token(drop(_buffer, 1), Token::colon);
|
|
return;
|
|
case '&':
|
|
if (_buffer.startswith("&=")) {
|
|
tok = Token(drop(_buffer, 2), Token::ampequal);
|
|
return;
|
|
}
|
|
tok = Token(drop(_buffer, 1), Token::amp);
|
|
return;
|
|
case '|':
|
|
if (_buffer.startswith("|=")) {
|
|
tok = Token(drop(_buffer, 2), Token::pipeequal);
|
|
return;
|
|
}
|
|
tok = Token(drop(_buffer, 1), Token::pipe);
|
|
return;
|
|
case '+':
|
|
if (_buffer.startswith("+=")) {
|
|
tok = Token(drop(_buffer, 2), Token::plusequal);
|
|
return;
|
|
}
|
|
tok = Token(drop(_buffer, 1), Token::plus);
|
|
return;
|
|
case '-': {
|
|
if (_buffer.startswith("-=")) {
|
|
tok = Token(drop(_buffer, 2), Token::minusequal);
|
|
return;
|
|
}
|
|
if (!_buffer.startswith("-l")) {
|
|
tok = Token(drop(_buffer, 1), Token::minus);
|
|
return;
|
|
}
|
|
// -l<lib name>
|
|
_buffer = _buffer.drop_front(2);
|
|
StringRef::size_type start = 0;
|
|
if (_buffer[start] == ':')
|
|
++start;
|
|
if (!canStartName(_buffer[start]))
|
|
// Create 'unknown' token.
|
|
break;
|
|
auto libNameEnd = std::find_if(_buffer.begin() + start + 1, _buffer.end(),
|
|
[=](char c) { return !canContinueName(c); });
|
|
StringRef::size_type libNameLen =
|
|
std::distance(_buffer.begin(), libNameEnd);
|
|
tok = Token(_buffer.substr(0, libNameLen), Token::libname);
|
|
_buffer = _buffer.drop_front(libNameLen);
|
|
return;
|
|
}
|
|
case '<':
|
|
if (_buffer.startswith("<<=")) {
|
|
tok = Token(drop(_buffer, 3), Token::lesslessequal);
|
|
return;
|
|
}
|
|
if (_buffer.startswith("<<")) {
|
|
tok = Token(drop(_buffer, 2), Token::lessless);
|
|
return;
|
|
}
|
|
if (_buffer.startswith("<=")) {
|
|
tok = Token(drop(_buffer, 2), Token::lessequal);
|
|
return;
|
|
}
|
|
tok = Token(drop(_buffer, 1), Token::less);
|
|
return;
|
|
case '>':
|
|
if (_buffer.startswith(">>=")) {
|
|
tok = Token(drop(_buffer, 3), Token::greatergreaterequal);
|
|
return;
|
|
}
|
|
if (_buffer.startswith(">>")) {
|
|
tok = Token(drop(_buffer, 2), Token::greatergreater);
|
|
return;
|
|
}
|
|
if (_buffer.startswith(">=")) {
|
|
tok = Token(drop(_buffer, 2), Token::greaterequal);
|
|
return;
|
|
}
|
|
tok = Token(drop(_buffer, 1), Token::greater);
|
|
return;
|
|
case '~':
|
|
tok = Token(drop(_buffer, 1), Token::tilde);
|
|
return;
|
|
case '\"': case '\'': {
|
|
// Handle quoted strings. They are treated as identifiers for
|
|
// simplicity.
|
|
char c = _buffer[0];
|
|
_buffer = _buffer.drop_front();
|
|
auto quotedStringEnd = _buffer.find(c);
|
|
if (quotedStringEnd == StringRef::npos || quotedStringEnd == 0)
|
|
break;
|
|
StringRef word = _buffer.substr(0, quotedStringEnd);
|
|
tok = Token(word, Token::identifier);
|
|
_buffer = _buffer.drop_front(quotedStringEnd + 1);
|
|
return;
|
|
}
|
|
default:
|
|
// Handle literal numbers
|
|
if (canStartNumber(_buffer[0])) {
|
|
auto endIter = std::find_if(_buffer.begin(), _buffer.end(), [=](char c) {
|
|
return !canContinueNumber(c);
|
|
});
|
|
StringRef::size_type end = endIter == _buffer.end()
|
|
? StringRef::npos
|
|
: std::distance(_buffer.begin(), endIter);
|
|
if (end == StringRef::npos || end == 0)
|
|
break;
|
|
StringRef word = _buffer.substr(0, end);
|
|
tok = Token(word, Token::number);
|
|
_buffer = _buffer.drop_front(end);
|
|
return;
|
|
}
|
|
// Handle slashes '/', which can be either an operator inside an expression
|
|
// or the beginning of an identifier
|
|
if (_buffer.startswith("/=")) {
|
|
tok = Token(drop(_buffer, 2), Token::slashequal);
|
|
return;
|
|
}
|
|
if (_buffer[0] == '/' && _buffer.size() > 1 &&
|
|
!canContinueName(_buffer[1])) {
|
|
tok = Token(drop(_buffer, 1), Token::slash);
|
|
return;
|
|
}
|
|
// Handle stars '*'
|
|
if (_buffer.startswith("*=")) {
|
|
tok = Token(drop(_buffer, 2), Token::starequal);
|
|
return;
|
|
}
|
|
if (_buffer[0] == '*' && _buffer.size() > 1 &&
|
|
!canContinueName(_buffer[1])) {
|
|
tok = Token(drop(_buffer, 1), Token::star);
|
|
return;
|
|
}
|
|
// Handle questions '?'
|
|
if (_buffer[0] == '?' && _buffer.size() > 1 &&
|
|
!canContinueName(_buffer[1])) {
|
|
tok = Token(drop(_buffer, 1), Token::question);
|
|
return;
|
|
}
|
|
// keyword or identifier.
|
|
if (!canStartName(_buffer[0]))
|
|
break;
|
|
auto endIter = std::find_if(_buffer.begin() + 1, _buffer.end(),
|
|
[=](char c) { return !canContinueName(c); });
|
|
StringRef::size_type end = endIter == _buffer.end()
|
|
? StringRef::npos
|
|
: std::distance(_buffer.begin(), endIter);
|
|
if (end == StringRef::npos || end == 0)
|
|
break;
|
|
StringRef word = _buffer.substr(0, end);
|
|
Token::Kind kind =
|
|
llvm::StringSwitch<Token::Kind>(word)
|
|
.Case("ALIGN", Token::kw_align)
|
|
.Case("ALIGN_WITH_INPUT", Token::kw_align_with_input)
|
|
.Case("AS_NEEDED", Token::kw_as_needed)
|
|
.Case("AT", Token::kw_at)
|
|
.Case("ENTRY", Token::kw_entry)
|
|
.Case("EXCLUDE_FILE", Token::kw_exclude_file)
|
|
.Case("EXTERN", Token::kw_extern)
|
|
.Case("FILL", Token::kw_fill)
|
|
.Case("GROUP", Token::kw_group)
|
|
.Case("HIDDEN", Token::kw_hidden)
|
|
.Case("INPUT", Token::kw_input)
|
|
.Case("KEEP", Token::kw_keep)
|
|
.Case("LENGTH", Token::kw_length)
|
|
.Case("l", Token::kw_length)
|
|
.Case("len", Token::kw_length)
|
|
.Case("MEMORY", Token::kw_memory)
|
|
.Case("ONLY_IF_RO", Token::kw_only_if_ro)
|
|
.Case("ONLY_IF_RW", Token::kw_only_if_rw)
|
|
.Case("ORIGIN", Token::kw_origin)
|
|
.Case("o", Token::kw_origin)
|
|
.Case("org", Token::kw_origin)
|
|
.Case("OUTPUT", Token::kw_output)
|
|
.Case("OUTPUT_ARCH", Token::kw_output_arch)
|
|
.Case("OUTPUT_FORMAT", Token::kw_output_format)
|
|
.Case("OVERLAY", Token::kw_overlay)
|
|
.Case("PHDRS", Token::kw_phdrs)
|
|
.Case("PROVIDE", Token::kw_provide)
|
|
.Case("PROVIDE_HIDDEN", Token::kw_provide_hidden)
|
|
.Case("SEARCH_DIR", Token::kw_search_dir)
|
|
.Case("SECTIONS", Token::kw_sections)
|
|
.Case("SORT", Token::kw_sort_by_name)
|
|
.Case("SORT_BY_ALIGNMENT", Token::kw_sort_by_alignment)
|
|
.Case("SORT_BY_INIT_PRIORITY", Token::kw_sort_by_init_priority)
|
|
.Case("SORT_BY_NAME", Token::kw_sort_by_name)
|
|
.Case("SORT_NONE", Token::kw_sort_none)
|
|
.Case("SUBALIGN", Token::kw_subalign)
|
|
.Case("/DISCARD/", Token::kw_discard)
|
|
.Default(Token::identifier);
|
|
tok = Token(word, kind);
|
|
_buffer = _buffer.drop_front(end);
|
|
return;
|
|
}
|
|
tok = Token(drop(_buffer, 1), Token::unknown);
|
|
}
|
|
|
|
void Lexer::skipWhitespace() {
|
|
while (true) {
|
|
if (_buffer.empty())
|
|
return;
|
|
switch (_buffer[0]) {
|
|
case ' ':
|
|
case '\r':
|
|
case '\n':
|
|
case '\t':
|
|
_buffer = _buffer.drop_front();
|
|
break;
|
|
// Potential comment.
|
|
case '/':
|
|
if (_buffer.size() <= 1 || _buffer[1] != '*')
|
|
return;
|
|
// Skip starting /*
|
|
_buffer = _buffer.drop_front(2);
|
|
// If the next char is also a /, it's not the end.
|
|
if (!_buffer.empty() && _buffer[0] == '/')
|
|
_buffer = _buffer.drop_front();
|
|
|
|
// Scan for /'s. We're done if it is preceded by a *.
|
|
while (true) {
|
|
if (_buffer.empty())
|
|
break;
|
|
_buffer = _buffer.drop_front();
|
|
if (_buffer.data()[-1] == '/' && _buffer.data()[-2] == '*')
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Constant functions
|
|
void Constant::dump(raw_ostream &os) const { os << _num; }
|
|
|
|
ErrorOr<int64_t> Constant::evalExpr(const SymbolTableTy &symbolTable) const {
|
|
return _num;
|
|
}
|
|
|
|
// Symbol functions
|
|
void Symbol::dump(raw_ostream &os) const { os << _name; }
|
|
|
|
ErrorOr<int64_t> Symbol::evalExpr(const SymbolTableTy &symbolTable) const {
|
|
auto it = symbolTable.find(_name);
|
|
if (it == symbolTable.end())
|
|
return LinkerScriptReaderError::unknown_symbol_in_expr;
|
|
return it->second;
|
|
}
|
|
|
|
// FunctionCall functions
|
|
void FunctionCall::dump(raw_ostream &os) const {
|
|
os << _name << "(";
|
|
for (unsigned i = 0, e = _args.size(); i != e; ++i) {
|
|
if (i)
|
|
os << ", ";
|
|
_args[i]->dump(os);
|
|
}
|
|
os << ")";
|
|
}
|
|
|
|
ErrorOr<int64_t>
|
|
FunctionCall::evalExpr(const SymbolTableTy &symbolTable) const {
|
|
return LinkerScriptReaderError::unrecognized_function_in_expr;
|
|
}
|
|
|
|
// Unary functions
|
|
void Unary::dump(raw_ostream &os) const {
|
|
os << "(";
|
|
if (_op == Unary::Minus)
|
|
os << "-";
|
|
else
|
|
os << "~";
|
|
_child->dump(os);
|
|
os << ")";
|
|
}
|
|
|
|
ErrorOr<int64_t> Unary::evalExpr(const SymbolTableTy &symbolTable) const {
|
|
auto child = _child->evalExpr(symbolTable);
|
|
if (child.getError())
|
|
return child.getError();
|
|
|
|
int64_t childRes = *child;
|
|
switch (_op) {
|
|
case Unary::Minus:
|
|
return -childRes;
|
|
case Unary::Not:
|
|
return ~childRes;
|
|
}
|
|
|
|
llvm_unreachable("");
|
|
}
|
|
|
|
// BinOp functions
|
|
void BinOp::dump(raw_ostream &os) const {
|
|
os << "(";
|
|
_lhs->dump(os);
|
|
os << " ";
|
|
switch (_op) {
|
|
case Sum:
|
|
os << "+";
|
|
break;
|
|
case Sub:
|
|
os << "-";
|
|
break;
|
|
case Mul:
|
|
os << "*";
|
|
break;
|
|
case Div:
|
|
os << "/";
|
|
break;
|
|
case Shl:
|
|
os << "<<";
|
|
break;
|
|
case Shr:
|
|
os << ">>";
|
|
break;
|
|
case And:
|
|
os << "&";
|
|
break;
|
|
case Or:
|
|
os << "|";
|
|
break;
|
|
case CompareEqual:
|
|
os << "==";
|
|
break;
|
|
case CompareDifferent:
|
|
os << "!=";
|
|
break;
|
|
case CompareLess:
|
|
os << "<";
|
|
break;
|
|
case CompareGreater:
|
|
os << ">";
|
|
break;
|
|
case CompareLessEqual:
|
|
os << "<=";
|
|
break;
|
|
case CompareGreaterEqual:
|
|
os << ">=";
|
|
break;
|
|
}
|
|
os << " ";
|
|
_rhs->dump(os);
|
|
os << ")";
|
|
}
|
|
|
|
ErrorOr<int64_t> BinOp::evalExpr(const SymbolTableTy &symbolTable) const {
|
|
auto lhs = _lhs->evalExpr(symbolTable);
|
|
if (lhs.getError())
|
|
return lhs.getError();
|
|
auto rhs = _rhs->evalExpr(symbolTable);
|
|
if (rhs.getError())
|
|
return rhs.getError();
|
|
|
|
int64_t lhsRes = *lhs;
|
|
int64_t rhsRes = *rhs;
|
|
|
|
switch(_op) {
|
|
case And: return lhsRes & rhsRes;
|
|
case CompareDifferent: return lhsRes != rhsRes;
|
|
case CompareEqual: return lhsRes == rhsRes;
|
|
case CompareGreater: return lhsRes > rhsRes;
|
|
case CompareGreaterEqual: return lhsRes >= rhsRes;
|
|
case CompareLess: return lhsRes < rhsRes;
|
|
case CompareLessEqual: return lhsRes <= rhsRes;
|
|
case Div: return lhsRes / rhsRes;
|
|
case Mul: return lhsRes * rhsRes;
|
|
case Or: return lhsRes | rhsRes;
|
|
case Shl: return lhsRes << rhsRes;
|
|
case Shr: return lhsRes >> rhsRes;
|
|
case Sub: return lhsRes - rhsRes;
|
|
case Sum: return lhsRes + rhsRes;
|
|
}
|
|
|
|
llvm_unreachable("");
|
|
}
|
|
|
|
// TernaryConditional functions
|
|
void TernaryConditional::dump(raw_ostream &os) const {
|
|
_conditional->dump(os);
|
|
os << " ? ";
|
|
_trueExpr->dump(os);
|
|
os << " : ";
|
|
_falseExpr->dump(os);
|
|
}
|
|
|
|
ErrorOr<int64_t>
|
|
TernaryConditional::evalExpr(const SymbolTableTy &symbolTable) const {
|
|
auto conditional = _conditional->evalExpr(symbolTable);
|
|
if (conditional.getError())
|
|
return conditional.getError();
|
|
if (*conditional)
|
|
return _trueExpr->evalExpr(symbolTable);
|
|
return _falseExpr->evalExpr(symbolTable);
|
|
}
|
|
|
|
// SymbolAssignment functions
|
|
void SymbolAssignment::dump(raw_ostream &os) const {
|
|
int numParen = 0;
|
|
|
|
if (_assignmentVisibility != Default) {
|
|
switch (_assignmentVisibility) {
|
|
case Hidden:
|
|
os << "HIDDEN(";
|
|
break;
|
|
case Provide:
|
|
os << "PROVIDE(";
|
|
break;
|
|
case ProvideHidden:
|
|
os << "PROVIDE_HIDDEN(";
|
|
break;
|
|
default:
|
|
llvm_unreachable("Unknown visibility");
|
|
}
|
|
++numParen;
|
|
}
|
|
|
|
os << _symbol << " ";
|
|
switch (_assignmentKind) {
|
|
case Simple:
|
|
os << "=";
|
|
break;
|
|
case Sum:
|
|
os << "+=";
|
|
break;
|
|
case Sub:
|
|
os << "-=";
|
|
break;
|
|
case Mul:
|
|
os << "*=";
|
|
break;
|
|
case Div:
|
|
os << "/=";
|
|
break;
|
|
case Shl:
|
|
os << "<<=";
|
|
break;
|
|
case Shr:
|
|
os << ">>=";
|
|
break;
|
|
case And:
|
|
os << "&=";
|
|
break;
|
|
case Or:
|
|
os << "|=";
|
|
break;
|
|
}
|
|
|
|
os << " ";
|
|
_expression->dump(os);
|
|
if (numParen)
|
|
os << ")";
|
|
os << ";";
|
|
}
|
|
|
|
static int dumpSortDirectives(raw_ostream &os, WildcardSortMode sortMode) {
|
|
switch (sortMode) {
|
|
case WildcardSortMode::NA:
|
|
return 0;
|
|
case WildcardSortMode::ByName:
|
|
os << "SORT_BY_NAME(";
|
|
return 1;
|
|
case WildcardSortMode::ByAlignment:
|
|
os << "SORT_BY_ALIGNMENT(";
|
|
return 1;
|
|
case WildcardSortMode::ByInitPriority:
|
|
os << "SORT_BY_INIT_PRIORITY(";
|
|
return 1;
|
|
case WildcardSortMode::ByNameAndAlignment:
|
|
os << "SORT_BY_NAME(SORT_BY_ALIGNMENT(";
|
|
return 2;
|
|
case WildcardSortMode::ByAlignmentAndName:
|
|
os << "SORT_BY_ALIGNMENT(SORT_BY_NAME(";
|
|
return 2;
|
|
case WildcardSortMode::None:
|
|
os << "SORT_NONE(";
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// InputSectionName functions
|
|
void InputSectionName::dump(raw_ostream &os) const {
|
|
os << _name;
|
|
}
|
|
|
|
// InputSectionSortedGroup functions
|
|
static void dumpInputSections(raw_ostream &os,
|
|
llvm::ArrayRef<const InputSection *> secs) {
|
|
bool excludeFile = false;
|
|
bool first = true;
|
|
|
|
for (auto &secName : secs) {
|
|
if (!first)
|
|
os << " ";
|
|
first = false;
|
|
// Coalesce multiple input sections marked with EXCLUDE_FILE in the same
|
|
// EXCLUDE_FILE() group
|
|
if (auto inputSec = dyn_cast<InputSectionName>(secName)) {
|
|
if (!excludeFile && inputSec->hasExcludeFile()) {
|
|
excludeFile = true;
|
|
os << "EXCLUDE_FILE(";
|
|
} else if (excludeFile && !inputSec->hasExcludeFile()) {
|
|
excludeFile = false;
|
|
os << ") ";
|
|
}
|
|
}
|
|
secName->dump(os);
|
|
}
|
|
|
|
if (excludeFile)
|
|
os << ")";
|
|
}
|
|
|
|
void InputSectionSortedGroup::dump(raw_ostream &os) const {
|
|
int numParen = dumpSortDirectives(os, _sortMode);
|
|
dumpInputSections(os, _sections);
|
|
for (int i = 0; i < numParen; ++i)
|
|
os << ")";
|
|
}
|
|
|
|
// InputSectionsCmd functions
|
|
void InputSectionsCmd::dump(raw_ostream &os) const {
|
|
if (_keep)
|
|
os << "KEEP(";
|
|
|
|
int numParen = dumpSortDirectives(os, _fileSortMode);
|
|
os << _memberName;
|
|
for (int i = 0; i < numParen; ++i)
|
|
os << ")";
|
|
|
|
if (_archiveName.size() > 0) {
|
|
os << ":";
|
|
numParen = dumpSortDirectives(os, _archiveSortMode);
|
|
os << _archiveName;
|
|
for (int i = 0; i < numParen; ++i)
|
|
os << ")";
|
|
}
|
|
|
|
if (_sections.size() > 0) {
|
|
os << "(";
|
|
dumpInputSections(os, _sections);
|
|
os << ")";
|
|
}
|
|
|
|
if (_keep)
|
|
os << ")";
|
|
}
|
|
|
|
void FillCmd::dump(raw_ostream &os) const {
|
|
os << "FILL(";
|
|
dumpByteStream(os, StringRef((const char *)_bytes.begin(), _bytes.size()));
|
|
os << ")";
|
|
}
|
|
|
|
// OutputSectionDescription functions
|
|
void OutputSectionDescription::dump(raw_ostream &os) const {
|
|
if (_discard)
|
|
os << "/DISCARD/";
|
|
else
|
|
os << _sectionName;
|
|
|
|
if (_address) {
|
|
os << " ";
|
|
_address->dump(os);
|
|
}
|
|
os << " :\n";
|
|
|
|
if (_at) {
|
|
os << " AT(";
|
|
_at->dump(os);
|
|
os << ")\n";
|
|
}
|
|
|
|
if (_align) {
|
|
os << " ALIGN(";
|
|
_align->dump(os);
|
|
os << ")\n";
|
|
} else if (_alignWithInput) {
|
|
os << " ALIGN_WITH_INPUT\n";
|
|
}
|
|
|
|
if (_subAlign) {
|
|
os << " SUBALIGN(";
|
|
_subAlign->dump(os);
|
|
os << ")\n";
|
|
}
|
|
|
|
switch (_constraint) {
|
|
case C_None:
|
|
break;
|
|
case C_OnlyIfRO:
|
|
os << "ONLY_IF_RO";
|
|
break;
|
|
case C_OnlyIfRW:
|
|
os << "ONLY_IF_RW";
|
|
break;
|
|
}
|
|
|
|
os << " {\n";
|
|
for (auto &command : _outputSectionCommands) {
|
|
os << " ";
|
|
command->dump(os);
|
|
os << "\n";
|
|
}
|
|
os << " }";
|
|
|
|
for (auto && phdr : _phdrs)
|
|
os << " : " << phdr;
|
|
|
|
if (_fillStream.size() > 0) {
|
|
os << " =";
|
|
dumpByteStream(os, _fillStream);
|
|
} else if (_fillExpr) {
|
|
os << " =";
|
|
_fillExpr->dump(os);
|
|
}
|
|
}
|
|
|
|
// 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();
|
|
Symbol *sym = new (_alloc) Symbol(*this, _tok._range);
|
|
consumeToken();
|
|
return sym;
|
|
}
|
|
case Token::kw_align:
|
|
return parseFunctionCall();
|
|
case Token::minus:
|
|
consumeToken();
|
|
return new (_alloc) Unary(*this, Unary::Minus, parseExprOperand());
|
|
case Token::tilde:
|
|
consumeToken();
|
|
return new (_alloc) Unary(*this, Unary::Not, parseExprOperand());
|
|
case Token::number: {
|
|
auto val = parseNum(_tok._range);
|
|
if (val.getError()) {
|
|
error(_tok, "Unrecognized number constant");
|
|
return nullptr;
|
|
}
|
|
Constant *c = new (_alloc) Constant(*this, *val);
|
|
consumeToken();
|
|
return c;
|
|
}
|
|
case Token::l_paren: {
|
|
consumeToken();
|
|
const Expression *expr = parseExpression();
|
|
if (!expectAndConsume(Token::r_paren, "expected )"))
|
|
return nullptr;
|
|
return expr;
|
|
}
|
|
default:
|
|
llvm_unreachable("Unknown token");
|
|
}
|
|
}
|
|
|
|
static bool TokenToBinOp(const Token &tok, BinOp::Operation &op,
|
|
unsigned &precedence) {
|
|
switch (tok._kind) {
|
|
case Token::star:
|
|
op = BinOp::Mul;
|
|
precedence = 3;
|
|
return true;
|
|
case Token::slash:
|
|
op = BinOp::Div;
|
|
precedence = 3;
|
|
return true;
|
|
case Token::plus:
|
|
op = BinOp::Sum;
|
|
precedence = 4;
|
|
return true;
|
|
case Token::minus:
|
|
op = BinOp::Sub;
|
|
precedence = 4;
|
|
return true;
|
|
case Token::lessless:
|
|
op = BinOp::Shl;
|
|
precedence = 5;
|
|
return true;
|
|
case Token::greatergreater:
|
|
op = BinOp::Shr;
|
|
precedence = 5;
|
|
return true;
|
|
case Token::less:
|
|
op = BinOp::CompareLess;
|
|
precedence = 6;
|
|
return true;
|
|
case Token::greater:
|
|
op = BinOp::CompareGreater;
|
|
precedence = 6;
|
|
return true;
|
|
case Token::lessequal:
|
|
op = BinOp::CompareLessEqual;
|
|
precedence = 6;
|
|
return true;
|
|
case Token::greaterequal:
|
|
op = BinOp::CompareGreaterEqual;
|
|
precedence = 6;
|
|
return true;
|
|
case Token::equalequal:
|
|
op = BinOp::CompareEqual;
|
|
precedence = 7;
|
|
return true;
|
|
case Token::exclaimequal:
|
|
op = BinOp::CompareDifferent;
|
|
precedence = 7;
|
|
return true;
|
|
case Token::amp:
|
|
op = BinOp::And;
|
|
precedence = 8;
|
|
return true;
|
|
case Token::pipe:
|
|
op = BinOp::Or;
|
|
precedence = 10;
|
|
return true;
|
|
default:
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool isExpressionOperator(Token tok) {
|
|
switch (tok._kind) {
|
|
case Token::star:
|
|
case Token::slash:
|
|
case Token::plus:
|
|
case Token::minus:
|
|
case Token::lessless:
|
|
case Token::greatergreater:
|
|
case Token::less:
|
|
case Token::greater:
|
|
case Token::lessequal:
|
|
case Token::greaterequal:
|
|
case Token::equalequal:
|
|
case Token::exclaimequal:
|
|
case Token::amp:
|
|
case Token::pipe:
|
|
case Token::question:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
const Expression *Parser::parseExpression(unsigned precedence) {
|
|
assert(precedence <= 13 && "Invalid precedence value");
|
|
if (!expectExprOperand())
|
|
return nullptr;
|
|
|
|
const Expression *expr = parseExprOperand();
|
|
if (!expr)
|
|
return nullptr;
|
|
|
|
BinOp::Operation op;
|
|
unsigned binOpPrecedence = 0;
|
|
if (TokenToBinOp(_tok, op, binOpPrecedence)) {
|
|
if (precedence >= binOpPrecedence)
|
|
return parseOperatorOperandLoop(expr, precedence);
|
|
return expr;
|
|
}
|
|
|
|
// Non-binary operators
|
|
if (_tok._kind == Token::question && precedence >= 13)
|
|
return parseOperatorOperandLoop(expr, precedence);
|
|
return expr;
|
|
}
|
|
|
|
const Expression *Parser::parseOperatorOperandLoop(const Expression *lhs,
|
|
unsigned highestPrecedence) {
|
|
assert(highestPrecedence <= 13 && "Invalid precedence value");
|
|
unsigned precedence = 0;
|
|
const Expression *binOp = nullptr;
|
|
|
|
while (1) {
|
|
BinOp::Operation op;
|
|
if (!TokenToBinOp(_tok, op, precedence)) {
|
|
if (_tok._kind == Token::question && highestPrecedence >= 13)
|
|
return parseTernaryCondOp(lhs);
|
|
return binOp;
|
|
}
|
|
|
|
if (precedence > highestPrecedence)
|
|
return binOp;
|
|
|
|
consumeToken();
|
|
const Expression *rhs = parseExpression(precedence - 1);
|
|
if (!rhs)
|
|
return nullptr;
|
|
binOp = new (_alloc) BinOp(*this, lhs, op, rhs);
|
|
lhs = binOp;
|
|
}
|
|
}
|
|
|
|
const Expression *Parser::parseTernaryCondOp(const Expression *lhs) {
|
|
assert(_tok._kind == Token::question && "Expected question mark");
|
|
|
|
consumeToken();
|
|
|
|
// The ternary conditional operator has right-to-left associativity.
|
|
// To implement this, we allow our children to contain ternary conditional
|
|
// operators themselves (precedence 13).
|
|
const Expression *trueExpr = parseExpression(13);
|
|
if (!trueExpr)
|
|
return nullptr;
|
|
|
|
if (!expectAndConsume(Token::colon, "expected :"))
|
|
return nullptr;
|
|
|
|
const Expression *falseExpr = parseExpression(13);
|
|
if (!falseExpr)
|
|
return nullptr;
|
|
|
|
return new (_alloc) TernaryConditional(*this, lhs, trueExpr, falseExpr);
|
|
}
|
|
|
|
// Parse OUTPUT(ident)
|
|
Output *Parser::parseOutput() {
|
|
assert(_tok._kind == Token::kw_output && "Expected OUTPUT");
|
|
consumeToken();
|
|
if (!expectAndConsume(Token::l_paren, "expected ("))
|
|
return nullptr;
|
|
|
|
if (_tok._kind != Token::identifier) {
|
|
error(_tok, "Expected identifier in OUTPUT.");
|
|
return nullptr;
|
|
}
|
|
|
|
auto ret = new (_alloc) Output(*this, _tok._range);
|
|
consumeToken();
|
|
|
|
if (!expectAndConsume(Token::r_paren, "expected )"))
|
|
return nullptr;
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Parse OUTPUT_FORMAT(ident)
|
|
OutputFormat *Parser::parseOutputFormat() {
|
|
assert(_tok._kind == Token::kw_output_format && "Expected OUTPUT_FORMAT!");
|
|
consumeToken();
|
|
if (!expectAndConsume(Token::l_paren, "expected ("))
|
|
return nullptr;
|
|
|
|
if (_tok._kind != Token::identifier) {
|
|
error(_tok, "Expected identifier in OUTPUT_FORMAT.");
|
|
return nullptr;
|
|
}
|
|
|
|
SmallVector<StringRef, 8> formats;
|
|
formats.push_back(_tok._range);
|
|
|
|
consumeToken();
|
|
|
|
do {
|
|
if (isNextToken(Token::comma))
|
|
consumeToken();
|
|
else
|
|
break;
|
|
if (_tok._kind != Token::identifier) {
|
|
error(_tok, "Expected identifier in OUTPUT_FORMAT.");
|
|
return nullptr;
|
|
}
|
|
formats.push_back(_tok._range);
|
|
consumeToken();
|
|
} while (isNextToken(Token::comma));
|
|
|
|
if (!expectAndConsume(Token::r_paren, "expected )"))
|
|
return nullptr;
|
|
|
|
return new (_alloc) OutputFormat(*this, formats);
|
|
}
|
|
|
|
// Parse OUTPUT_ARCH(ident)
|
|
OutputArch *Parser::parseOutputArch() {
|
|
assert(_tok._kind == Token::kw_output_arch && "Expected OUTPUT_ARCH!");
|
|
consumeToken();
|
|
if (!expectAndConsume(Token::l_paren, "expected ("))
|
|
return nullptr;
|
|
|
|
if (_tok._kind != Token::identifier) {
|
|
error(_tok, "Expected identifier in OUTPUT_ARCH.");
|
|
return nullptr;
|
|
}
|
|
|
|
auto ret = new (_alloc) OutputArch(*this, _tok._range);
|
|
consumeToken();
|
|
|
|
if (!expectAndConsume(Token::r_paren, "expected )"))
|
|
return nullptr;
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Parse file list for INPUT or GROUP
|
|
template<class T> T *Parser::parsePathList() {
|
|
consumeToken();
|
|
if (!expectAndConsume(Token::l_paren, "expected ("))
|
|
return nullptr;
|
|
|
|
SmallVector<Path, 8> paths;
|
|
while (_tok._kind == Token::identifier || _tok._kind == Token::libname ||
|
|
_tok._kind == Token::kw_as_needed) {
|
|
switch (_tok._kind) {
|
|
case Token::identifier:
|
|
paths.push_back(Path(_tok._range));
|
|
consumeToken();
|
|
break;
|
|
case Token::libname:
|
|
paths.push_back(Path(_tok._range, false, true));
|
|
consumeToken();
|
|
break;
|
|
case Token::kw_as_needed:
|
|
if (!parseAsNeeded(paths))
|
|
return nullptr;
|
|
break;
|
|
default:
|
|
llvm_unreachable("Invalid token.");
|
|
}
|
|
}
|
|
if (!expectAndConsume(Token::r_paren, "expected )"))
|
|
return nullptr;
|
|
return new (_alloc) T(*this, paths);
|
|
}
|
|
|
|
// Parse AS_NEEDED(file ...)
|
|
bool Parser::parseAsNeeded(SmallVectorImpl<Path> &paths) {
|
|
assert(_tok._kind == Token::kw_as_needed && "Expected AS_NEEDED!");
|
|
consumeToken();
|
|
if (!expectAndConsume(Token::l_paren, "expected ("))
|
|
return false;
|
|
|
|
while (_tok._kind == Token::identifier || _tok._kind == Token::libname) {
|
|
switch (_tok._kind) {
|
|
case Token::identifier:
|
|
paths.push_back(Path(_tok._range, true, false));
|
|
consumeToken();
|
|
break;
|
|
case Token::libname:
|
|
paths.push_back(Path(_tok._range, true, true));
|
|
consumeToken();
|
|
break;
|
|
default:
|
|
llvm_unreachable("Invalid token.");
|
|
}
|
|
}
|
|
|
|
if (!expectAndConsume(Token::r_paren, "expected )"))
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
// Parse ENTRY(ident)
|
|
Entry *Parser::parseEntry() {
|
|
assert(_tok._kind == Token::kw_entry && "Expected ENTRY!");
|
|
consumeToken();
|
|
if (!expectAndConsume(Token::l_paren, "expected ("))
|
|
return nullptr;
|
|
if (_tok._kind != Token::identifier) {
|
|
error(_tok, "expected identifier in ENTRY");
|
|
return nullptr;
|
|
}
|
|
StringRef entryName(_tok._range);
|
|
consumeToken();
|
|
if (!expectAndConsume(Token::r_paren, "expected )"))
|
|
return nullptr;
|
|
return new (_alloc) Entry(*this, entryName);
|
|
}
|
|
|
|
// Parse SEARCH_DIR(ident)
|
|
SearchDir *Parser::parseSearchDir() {
|
|
assert(_tok._kind == Token::kw_search_dir && "Expected SEARCH_DIR!");
|
|
consumeToken();
|
|
if (!expectAndConsume(Token::l_paren, "expected ("))
|
|
return nullptr;
|
|
if (_tok._kind != Token::identifier) {
|
|
error(_tok, "expected identifier in SEARCH_DIR");
|
|
return nullptr;
|
|
}
|
|
StringRef searchPath(_tok._range);
|
|
consumeToken();
|
|
if (!expectAndConsume(Token::r_paren, "expected )"))
|
|
return nullptr;
|
|
return new (_alloc) SearchDir(*this, searchPath);
|
|
}
|
|
|
|
const SymbolAssignment *Parser::parseSymbolAssignment() {
|
|
assert((_tok._kind == Token::identifier || _tok._kind == Token::kw_hidden ||
|
|
_tok._kind == Token::kw_provide ||
|
|
_tok._kind == Token::kw_provide_hidden) &&
|
|
"Expected identifier!");
|
|
SymbolAssignment::AssignmentVisibility visibility = SymbolAssignment::Default;
|
|
SymbolAssignment::AssignmentKind kind;
|
|
int numParen = 0;
|
|
|
|
switch (_tok._kind) {
|
|
case Token::kw_hidden:
|
|
visibility = SymbolAssignment::Hidden;
|
|
++numParen;
|
|
consumeToken();
|
|
if (!expectAndConsume(Token::l_paren, "expected ("))
|
|
return nullptr;
|
|
break;
|
|
case Token::kw_provide:
|
|
visibility = SymbolAssignment::Provide;
|
|
++numParen;
|
|
consumeToken();
|
|
if (!expectAndConsume(Token::l_paren, "expected ("))
|
|
return nullptr;
|
|
break;
|
|
case Token::kw_provide_hidden:
|
|
visibility = SymbolAssignment::ProvideHidden;
|
|
++numParen;
|
|
consumeToken();
|
|
if (!expectAndConsume(Token::l_paren, "expected ("))
|
|
return nullptr;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
StringRef name = _tok._range;
|
|
consumeToken();
|
|
|
|
// Parse assignment operator (=, +=, -= etc.)
|
|
switch (_tok._kind) {
|
|
case Token::equal:
|
|
kind = SymbolAssignment::Simple;
|
|
break;
|
|
case Token::plusequal:
|
|
kind = SymbolAssignment::Sum;
|
|
break;
|
|
case Token::minusequal:
|
|
kind = SymbolAssignment::Sub;
|
|
break;
|
|
case Token::starequal:
|
|
kind = SymbolAssignment::Mul;
|
|
break;
|
|
case Token::slashequal:
|
|
kind = SymbolAssignment::Div;
|
|
break;
|
|
case Token::ampequal:
|
|
kind = SymbolAssignment::And;
|
|
break;
|
|
case Token::pipeequal:
|
|
kind = SymbolAssignment::Or;
|
|
break;
|
|
case Token::lesslessequal:
|
|
kind = SymbolAssignment::Shl;
|
|
break;
|
|
case Token::greatergreaterequal:
|
|
kind = SymbolAssignment::Shr;
|
|
break;
|
|
default:
|
|
error(_tok, "unexpected token");
|
|
return nullptr;
|
|
}
|
|
|
|
consumeToken();
|
|
|
|
const Expression *expr = nullptr;
|
|
switch (_tok._kind) {
|
|
case Token::number:
|
|
case Token::kw_align:
|
|
case Token::identifier:
|
|
case Token::l_paren:
|
|
expr = parseExpression();
|
|
if (!expr)
|
|
return nullptr;
|
|
break;
|
|
default:
|
|
error(_tok, "unexpected token while parsing assignment value.");
|
|
return nullptr;
|
|
}
|
|
|
|
for (int i = 0; i < numParen; ++i)
|
|
if (!expectAndConsume(Token::r_paren, "expected )"))
|
|
return nullptr;
|
|
|
|
return new (_alloc) SymbolAssignment(*this, name, expr, kind, visibility);
|
|
}
|
|
|
|
llvm::ErrorOr<InputSectionsCmd::VectorTy> Parser::parseExcludeFile() {
|
|
assert(_tok._kind == Token::kw_exclude_file && "Expected EXCLUDE_FILE!");
|
|
InputSectionsCmd::VectorTy res;
|
|
consumeToken();
|
|
|
|
if (!expectAndConsume(Token::l_paren, "expected ("))
|
|
return llvm::ErrorOr<InputSectionsCmd::VectorTy>(
|
|
make_error_code(llvm::errc::io_error));
|
|
|
|
while (_tok._kind == Token::identifier) {
|
|
res.push_back(new (_alloc) InputSectionName(*this, _tok._range, true));
|
|
consumeToken();
|
|
}
|
|
|
|
if (!expectAndConsume(Token::r_paren, "expected )"))
|
|
return llvm::ErrorOr<InputSectionsCmd::VectorTy>(
|
|
make_error_code(llvm::errc::io_error));
|
|
return llvm::ErrorOr<InputSectionsCmd::VectorTy>(std::move(res));
|
|
}
|
|
|
|
int Parser::parseSortDirectives(WildcardSortMode &sortMode) {
|
|
int numParsedDirectives = 0;
|
|
sortMode = WildcardSortMode::NA;
|
|
|
|
if (_tok._kind == Token::kw_sort_by_name) {
|
|
consumeToken();
|
|
if (!expectAndConsume(Token::l_paren, "expected ("))
|
|
return -1;
|
|
++numParsedDirectives;
|
|
sortMode = WildcardSortMode::ByName;
|
|
}
|
|
|
|
if (_tok._kind == Token::kw_sort_by_init_priority) {
|
|
consumeToken();
|
|
if (!expectAndConsume(Token::l_paren, "expected ("))
|
|
return -1;
|
|
++numParsedDirectives;
|
|
sortMode = WildcardSortMode::ByInitPriority;
|
|
}
|
|
|
|
if (_tok._kind == Token::kw_sort_by_alignment) {
|
|
consumeToken();
|
|
if (!expectAndConsume(Token::l_paren, "expected ("))
|
|
return -1;
|
|
++numParsedDirectives;
|
|
if (sortMode != WildcardSortMode::ByName)
|
|
sortMode = WildcardSortMode::ByAlignment;
|
|
else
|
|
sortMode = WildcardSortMode::ByNameAndAlignment;
|
|
}
|
|
|
|
if (numParsedDirectives < 2 && _tok._kind == Token::kw_sort_by_name) {
|
|
consumeToken();
|
|
if (!expectAndConsume(Token::l_paren, "expected ("))
|
|
return -1;
|
|
++numParsedDirectives;
|
|
if (sortMode == WildcardSortMode::ByAlignment)
|
|
sortMode = WildcardSortMode::ByAlignmentAndName;
|
|
}
|
|
|
|
if (numParsedDirectives < 2 && _tok._kind == Token::kw_sort_by_alignment) {
|
|
consumeToken();
|
|
if (!expectAndConsume(Token::l_paren, "expected ("))
|
|
return -1;
|
|
++numParsedDirectives;
|
|
}
|
|
|
|
if (numParsedDirectives == 0 && _tok._kind == Token::kw_sort_none) {
|
|
consumeToken();
|
|
if (!expectAndConsume(Token::l_paren, "expected ("))
|
|
return -1;
|
|
++numParsedDirectives;
|
|
sortMode = WildcardSortMode::None;
|
|
}
|
|
|
|
return numParsedDirectives;
|
|
}
|
|
|
|
const InputSection *Parser::parseSortedInputSections() {
|
|
assert((_tok._kind == Token::kw_sort_by_name ||
|
|
_tok._kind == Token::kw_sort_by_alignment ||
|
|
_tok._kind == Token::kw_sort_by_init_priority ||
|
|
_tok._kind == Token::kw_sort_none) &&
|
|
"Expected SORT directives!");
|
|
|
|
WildcardSortMode sortMode = WildcardSortMode::NA;
|
|
int numParen = parseSortDirectives(sortMode);
|
|
if (numParen == -1)
|
|
return nullptr;
|
|
|
|
SmallVector<const InputSection *, 8> inputSections;
|
|
|
|
while (_tok._kind == Token::identifier) {
|
|
inputSections.push_back(new (_alloc)
|
|
InputSectionName(*this, _tok._range, false));
|
|
consumeToken();
|
|
}
|
|
|
|
// Eat "numParen" rparens
|
|
for (int i = 0, e = numParen; i != e; ++i)
|
|
if (!expectAndConsume(Token::r_paren, "expected )"))
|
|
return nullptr;
|
|
|
|
return new (_alloc) InputSectionSortedGroup(*this, sortMode, inputSections);
|
|
}
|
|
|
|
const InputSectionsCmd *Parser::parseInputSectionsCmd() {
|
|
assert((_tok._kind == Token::identifier || _tok._kind == Token::colon ||
|
|
_tok._kind == Token::star || _tok._kind == Token::kw_keep ||
|
|
_tok._kind == Token::kw_sort_by_name ||
|
|
_tok._kind == Token::kw_sort_by_alignment ||
|
|
_tok._kind == Token::kw_sort_by_init_priority ||
|
|
_tok._kind == Token::kw_sort_none) &&
|
|
"Expected input section first tokens!");
|
|
int numParen = 1;
|
|
bool keep = false;
|
|
WildcardSortMode fileSortMode = WildcardSortMode::NA;
|
|
WildcardSortMode archiveSortMode = WildcardSortMode::NA;
|
|
StringRef memberName;
|
|
StringRef archiveName;
|
|
|
|
if (_tok._kind == Token::kw_keep) {
|
|
consumeToken();
|
|
if (!expectAndConsume(Token::l_paren, "expected ("))
|
|
return nullptr;
|
|
++numParen;
|
|
keep = true;
|
|
}
|
|
|
|
// Input name
|
|
if (_tok._kind != Token::colon) {
|
|
int numParen = parseSortDirectives(fileSortMode);
|
|
if (numParen == -1)
|
|
return nullptr;
|
|
memberName = _tok._range;
|
|
consumeToken();
|
|
if (numParen) {
|
|
while (numParen--)
|
|
if (!expectAndConsume(Token::r_paren, "expected )"))
|
|
return nullptr;
|
|
}
|
|
}
|
|
if (_tok._kind == Token::colon) {
|
|
consumeToken();
|
|
if (_tok._kind == Token::identifier ||
|
|
_tok._kind == Token::kw_sort_by_name ||
|
|
_tok._kind == Token::kw_sort_by_alignment ||
|
|
_tok._kind == Token::kw_sort_by_init_priority ||
|
|
_tok._kind == Token::kw_sort_none) {
|
|
int numParen = parseSortDirectives(archiveSortMode);
|
|
if (numParen == -1)
|
|
return nullptr;
|
|
archiveName = _tok._range;
|
|
consumeToken();
|
|
for (int i = 0; i != numParen; ++i)
|
|
if (!expectAndConsume(Token::r_paren, "expected )"))
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
SmallVector<const InputSection *, 8> inputSections;
|
|
|
|
if (_tok._kind != Token::l_paren)
|
|
return new (_alloc)
|
|
InputSectionsCmd(*this, memberName, archiveName, keep, fileSortMode,
|
|
archiveSortMode, inputSections);
|
|
consumeToken();
|
|
|
|
while (_tok._kind == Token::identifier ||
|
|
_tok._kind == Token::kw_exclude_file ||
|
|
_tok._kind == Token::kw_sort_by_name ||
|
|
_tok._kind == Token::kw_sort_by_alignment ||
|
|
_tok._kind == Token::kw_sort_by_init_priority ||
|
|
_tok._kind == Token::kw_sort_none) {
|
|
switch (_tok._kind) {
|
|
case Token::kw_exclude_file: {
|
|
auto vec = parseExcludeFile();
|
|
if (vec.getError())
|
|
return nullptr;
|
|
inputSections.insert(inputSections.end(), vec->begin(), vec->end());
|
|
break;
|
|
}
|
|
case Token::star:
|
|
case Token::identifier: {
|
|
inputSections.push_back(new (_alloc)
|
|
InputSectionName(*this, _tok._range, false));
|
|
consumeToken();
|
|
break;
|
|
}
|
|
case Token::kw_sort_by_name:
|
|
case Token::kw_sort_by_alignment:
|
|
case Token::kw_sort_by_init_priority:
|
|
case Token::kw_sort_none: {
|
|
const InputSection *group = parseSortedInputSections();
|
|
if (!group)
|
|
return nullptr;
|
|
inputSections.push_back(group);
|
|
break;
|
|
}
|
|
default:
|
|
llvm_unreachable("Unknown token");
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < numParen; ++i)
|
|
if (!expectAndConsume(Token::r_paren, "expected )"))
|
|
return nullptr;
|
|
return new (_alloc)
|
|
InputSectionsCmd(*this, memberName, archiveName, keep, fileSortMode,
|
|
archiveSortMode, inputSections);
|
|
}
|
|
|
|
const FillCmd *Parser::parseFillCmd() {
|
|
assert(_tok._kind == Token::kw_fill && "Expected FILL!");
|
|
consumeToken();
|
|
if (!expectAndConsume(Token::l_paren, "expected ("))
|
|
return nullptr;
|
|
|
|
SmallVector<uint8_t, 8> storage;
|
|
|
|
// If the expression is just a number, it's arbitrary length.
|
|
if (_tok._kind == Token::number && peek()._kind == Token::r_paren) {
|
|
if (_tok._range.size() > 2 && _tok._range.startswith("0x")) {
|
|
StringRef num = _tok._range.substr(2);
|
|
for (char c : num) {
|
|
unsigned nibble = llvm::hexDigitValue(c);
|
|
if (nibble == -1u)
|
|
goto not_simple_hex;
|
|
storage.push_back(nibble);
|
|
}
|
|
|
|
if (storage.size() % 2 != 0)
|
|
storage.insert(storage.begin(), 0);
|
|
|
|
// Collapse nibbles.
|
|
for (std::size_t i = 0, e = storage.size() / 2; i != e; ++i)
|
|
storage[i] = (storage[i * 2] << 4) + storage[(i * 2) + 1];
|
|
|
|
storage.resize(storage.size() / 2);
|
|
}
|
|
}
|
|
not_simple_hex:
|
|
|
|
const Expression *expr = parseExpression();
|
|
if (!expr)
|
|
return nullptr;
|
|
if (!expectAndConsume(Token::r_paren, "expected )"))
|
|
return nullptr;
|
|
|
|
return new(getAllocator()) FillCmd(*this, storage);
|
|
}
|
|
|
|
const OutputSectionDescription *Parser::parseOutputSectionDescription() {
|
|
assert((_tok._kind == Token::kw_discard || _tok._kind == Token::identifier) &&
|
|
"Expected /DISCARD/ or identifier!");
|
|
StringRef sectionName;
|
|
const Expression *address = nullptr;
|
|
const Expression *align = nullptr;
|
|
const Expression *subAlign = nullptr;
|
|
const Expression *at = nullptr;
|
|
const Expression *fillExpr = nullptr;
|
|
StringRef fillStream;
|
|
bool alignWithInput = false;
|
|
bool discard = false;
|
|
OutputSectionDescription::Constraint constraint =
|
|
OutputSectionDescription::C_None;
|
|
SmallVector<const Command *, 8> outputSectionCommands;
|
|
|
|
if (_tok._kind == Token::kw_discard)
|
|
discard = true;
|
|
else
|
|
sectionName = _tok._range;
|
|
consumeToken();
|
|
|
|
if (_tok._kind == Token::number || _tok._kind == Token::identifier ||
|
|
_tok._kind == Token::kw_align || _tok._kind == Token::l_paren) {
|
|
address = parseExpression();
|
|
if (!address)
|
|
return nullptr;
|
|
}
|
|
|
|
if (!expectAndConsume(Token::colon, "expected :"))
|
|
return nullptr;
|
|
|
|
if (_tok._kind == Token::kw_at) {
|
|
consumeToken();
|
|
at = parseExpression();
|
|
if (!at)
|
|
return nullptr;
|
|
}
|
|
|
|
if (_tok._kind == Token::kw_align) {
|
|
consumeToken();
|
|
align = parseExpression();
|
|
if (!align)
|
|
return nullptr;
|
|
}
|
|
|
|
if (_tok._kind == Token::kw_align_with_input) {
|
|
consumeToken();
|
|
alignWithInput = true;
|
|
}
|
|
|
|
if (_tok._kind == Token::kw_subalign) {
|
|
consumeToken();
|
|
subAlign = parseExpression();
|
|
if (!subAlign)
|
|
return nullptr;
|
|
}
|
|
|
|
if (_tok._kind == Token::kw_only_if_ro) {
|
|
consumeToken();
|
|
constraint = OutputSectionDescription::C_OnlyIfRO;
|
|
} else if (_tok._kind == Token::kw_only_if_rw) {
|
|
consumeToken();
|
|
constraint = OutputSectionDescription::C_OnlyIfRW;
|
|
}
|
|
|
|
if (!expectAndConsume(Token::l_brace, "expected {"))
|
|
return nullptr;
|
|
|
|
// Parse zero or more output-section-commands
|
|
while (_tok._kind != Token::r_brace) {
|
|
switch (_tok._kind) {
|
|
case Token::semicolon:
|
|
consumeToken();
|
|
break;
|
|
case Token::identifier:
|
|
switch (peek()._kind) {
|
|
case Token::equal:
|
|
case Token::plusequal:
|
|
case Token::minusequal:
|
|
case Token::starequal:
|
|
case Token::slashequal:
|
|
case Token::ampequal:
|
|
case Token::pipeequal:
|
|
case Token::lesslessequal:
|
|
case Token::greatergreaterequal:
|
|
if (const Command *cmd = parseSymbolAssignment())
|
|
outputSectionCommands.push_back(cmd);
|
|
else
|
|
return nullptr;
|
|
break;
|
|
default:
|
|
if (const Command *cmd = parseInputSectionsCmd())
|
|
outputSectionCommands.push_back(cmd);
|
|
else
|
|
return nullptr;
|
|
break;
|
|
}
|
|
break;
|
|
case Token::kw_fill:
|
|
if (const Command *cmd = parseFillCmd())
|
|
outputSectionCommands.push_back(cmd);
|
|
else
|
|
return nullptr;
|
|
break;
|
|
case Token::kw_keep:
|
|
case Token::star:
|
|
case Token::colon:
|
|
case Token::kw_sort_by_name:
|
|
case Token::kw_sort_by_alignment:
|
|
case Token::kw_sort_by_init_priority:
|
|
case Token::kw_sort_none:
|
|
if (const Command *cmd = parseInputSectionsCmd())
|
|
outputSectionCommands.push_back(cmd);
|
|
else
|
|
return nullptr;
|
|
break;
|
|
case Token::kw_hidden:
|
|
case Token::kw_provide:
|
|
case Token::kw_provide_hidden:
|
|
if (const Command *cmd = parseSymbolAssignment())
|
|
outputSectionCommands.push_back(cmd);
|
|
else
|
|
return nullptr;
|
|
break;
|
|
default:
|
|
error(_tok, "expected symbol assignment or input file name.");
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
if (!expectAndConsume(Token::r_brace, "expected }"))
|
|
return nullptr;
|
|
|
|
SmallVector<StringRef, 2> phdrs;
|
|
while (_tok._kind == Token::colon) {
|
|
consumeToken();
|
|
if (_tok._kind != Token::identifier) {
|
|
error(_tok, "expected program header name");
|
|
return nullptr;
|
|
}
|
|
phdrs.push_back(_tok._range);
|
|
consumeToken();
|
|
}
|
|
|
|
if (_tok._kind == Token::equal) {
|
|
consumeToken();
|
|
if (_tok._kind != Token::number || !_tok._range.startswith_lower("0x")) {
|
|
fillExpr = parseExpression();
|
|
if (!fillExpr)
|
|
return nullptr;
|
|
} else {
|
|
std::string strBuf;
|
|
if (isExpressionOperator(peek()) ||
|
|
!parseHexToByteStream(_tok._range.drop_front(2), strBuf)) {
|
|
fillExpr = parseExpression();
|
|
if(!fillExpr)
|
|
return nullptr;
|
|
} else {
|
|
char *rawBuf = (char *) _alloc.Allocate(strBuf.size(), 1);
|
|
memcpy(rawBuf, strBuf.c_str(), strBuf.size());
|
|
fillStream = StringRef(rawBuf, strBuf.size());
|
|
consumeToken();
|
|
}
|
|
}
|
|
}
|
|
|
|
return new (_alloc) OutputSectionDescription(
|
|
*this, sectionName, address, align, subAlign, at, fillExpr, fillStream,
|
|
alignWithInput, discard, constraint, outputSectionCommands, phdrs);
|
|
}
|
|
|
|
const Overlay *Parser::parseOverlay() {
|
|
assert(_tok._kind == Token::kw_overlay && "Expected OVERLAY!");
|
|
error(_tok, "Overlay description is not yet supported.");
|
|
return nullptr;
|
|
}
|
|
|
|
const PHDR *Parser::parsePHDR() {
|
|
assert(_tok._kind == Token::identifier && "Expected identifier!");
|
|
|
|
StringRef name = _tok._range;
|
|
consumeToken();
|
|
|
|
uint64_t type;
|
|
|
|
switch (_tok._kind) {
|
|
case Token::identifier:
|
|
case Token::number:
|
|
case Token::l_paren: {
|
|
const Expression *expr = parseExpression();
|
|
if (!expr)
|
|
return nullptr;
|
|
Expression::SymbolTableTy PHDRTypes;
|
|
#define PHDR_INSERT(x) PHDRTypes.insert(std::make_pair(#x, llvm::ELF::x))
|
|
PHDR_INSERT(PT_NULL);
|
|
PHDR_INSERT(PT_LOAD);
|
|
PHDR_INSERT(PT_DYNAMIC);
|
|
PHDR_INSERT(PT_INTERP);
|
|
PHDR_INSERT(PT_NOTE);
|
|
PHDR_INSERT(PT_SHLIB);
|
|
PHDR_INSERT(PT_PHDR);
|
|
PHDR_INSERT(PT_TLS);
|
|
PHDR_INSERT(PT_LOOS);
|
|
PHDR_INSERT(PT_GNU_EH_FRAME);
|
|
PHDR_INSERT(PT_GNU_STACK);
|
|
PHDR_INSERT(PT_GNU_RELRO);
|
|
PHDR_INSERT(PT_SUNW_EH_FRAME);
|
|
PHDR_INSERT(PT_SUNW_UNWIND);
|
|
PHDR_INSERT(PT_HIOS);
|
|
PHDR_INSERT(PT_LOPROC);
|
|
PHDR_INSERT(PT_ARM_ARCHEXT);
|
|
PHDR_INSERT(PT_ARM_EXIDX);
|
|
PHDR_INSERT(PT_ARM_UNWIND);
|
|
PHDR_INSERT(PT_MIPS_REGINFO);
|
|
PHDR_INSERT(PT_MIPS_RTPROC);
|
|
PHDR_INSERT(PT_MIPS_OPTIONS);
|
|
PHDR_INSERT(PT_MIPS_ABIFLAGS);
|
|
PHDR_INSERT(PT_HIPROC);
|
|
#undef PHDR_INSERT
|
|
auto t = expr->evalExpr(PHDRTypes);
|
|
if (t == LinkerScriptReaderError::unknown_symbol_in_expr) {
|
|
error(_tok, "Unknown type");
|
|
return nullptr;
|
|
}
|
|
if (!t)
|
|
return nullptr;
|
|
type = *t;
|
|
break;
|
|
}
|
|
default:
|
|
error(_tok, "expected identifier or expression");
|
|
return nullptr;
|
|
}
|
|
|
|
uint64_t flags = 0;
|
|
|
|
if (_tok._kind == Token::identifier && _tok._range == "FLAGS") {
|
|
consumeToken();
|
|
if (!expectAndConsume(Token::l_paren, "Expected ("))
|
|
return nullptr;
|
|
const Expression *flagsExpr = parseExpression();
|
|
if (!flagsExpr)
|
|
return nullptr;
|
|
auto f = flagsExpr->evalExpr();
|
|
if (!f)
|
|
return nullptr;
|
|
flags = *f;
|
|
if (!expectAndConsume(Token::r_paren, "Expected )"))
|
|
return nullptr;
|
|
}
|
|
|
|
if (!expectAndConsume(Token::semicolon, "Expected ;"))
|
|
return nullptr;
|
|
|
|
return new (getAllocator()) PHDR(name, type, false, false, nullptr, flags);
|
|
}
|
|
|
|
PHDRS *Parser::parsePHDRS() {
|
|
assert(_tok._kind == Token::kw_phdrs && "Expected PHDRS!");
|
|
consumeToken();
|
|
if (!expectAndConsume(Token::l_brace, "expected {"))
|
|
return nullptr;
|
|
|
|
SmallVector<const PHDR *, 8> phdrs;
|
|
|
|
while (true) {
|
|
if (_tok._kind == Token::identifier) {
|
|
const PHDR *phdr = parsePHDR();
|
|
if (!phdr)
|
|
return nullptr;
|
|
phdrs.push_back(phdr);
|
|
} else
|
|
break;
|
|
}
|
|
|
|
if (!expectAndConsume(Token::r_brace, "expected }"))
|
|
return nullptr;
|
|
|
|
return new (getAllocator()) PHDRS(*this, phdrs);
|
|
}
|
|
|
|
Sections *Parser::parseSections() {
|
|
assert(_tok._kind == Token::kw_sections && "Expected SECTIONS!");
|
|
consumeToken();
|
|
if (!expectAndConsume(Token::l_brace, "expected {"))
|
|
return nullptr;
|
|
SmallVector<const Command *, 8> sectionsCommands;
|
|
|
|
bool unrecognizedToken = false;
|
|
// Parse zero or more sections-commands
|
|
while (!unrecognizedToken) {
|
|
switch (_tok._kind) {
|
|
case Token::semicolon:
|
|
consumeToken();
|
|
break;
|
|
|
|
case Token::identifier:
|
|
switch (peek()._kind) {
|
|
case Token::equal:
|
|
case Token::plusequal:
|
|
case Token::minusequal:
|
|
case Token::starequal:
|
|
case Token::slashequal:
|
|
case Token::ampequal:
|
|
case Token::pipeequal:
|
|
case Token::lesslessequal:
|
|
case Token::greatergreaterequal:
|
|
if (const Command *cmd = parseSymbolAssignment())
|
|
sectionsCommands.push_back(cmd);
|
|
else
|
|
return nullptr;
|
|
break;
|
|
default:
|
|
if (const Command *cmd = parseOutputSectionDescription())
|
|
sectionsCommands.push_back(cmd);
|
|
else
|
|
return nullptr;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case Token::kw_discard:
|
|
case Token::star:
|
|
if (const Command *cmd = parseOutputSectionDescription())
|
|
sectionsCommands.push_back(cmd);
|
|
else
|
|
return nullptr;
|
|
break;
|
|
|
|
case Token::kw_entry:
|
|
if (const Command *cmd = parseEntry())
|
|
sectionsCommands.push_back(cmd);
|
|
else
|
|
return nullptr;
|
|
break;
|
|
|
|
case Token::kw_hidden:
|
|
case Token::kw_provide:
|
|
case Token::kw_provide_hidden:
|
|
if (const Command *cmd = parseSymbolAssignment())
|
|
sectionsCommands.push_back(cmd);
|
|
else
|
|
return nullptr;
|
|
break;
|
|
|
|
case Token::kw_overlay:
|
|
if (const Command *cmd = parseOverlay())
|
|
sectionsCommands.push_back(cmd);
|
|
else
|
|
return nullptr;
|
|
break;
|
|
|
|
default:
|
|
unrecognizedToken = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!expectAndConsume(
|
|
Token::r_brace,
|
|
"expected symbol assignment, entry, overlay or output section name."))
|
|
return nullptr;
|
|
|
|
return new (_alloc) Sections(*this, sectionsCommands);
|
|
}
|
|
|
|
Memory *Parser::parseMemory() {
|
|
assert(_tok._kind == Token::kw_memory && "Expected MEMORY!");
|
|
consumeToken();
|
|
if (!expectAndConsume(Token::l_brace, "expected {"))
|
|
return nullptr;
|
|
SmallVector<const MemoryBlock *, 8> blocks;
|
|
|
|
bool unrecognizedToken = false;
|
|
// Parse zero or more memory block descriptors.
|
|
while (!unrecognizedToken) {
|
|
if (_tok._kind == Token::identifier) {
|
|
StringRef name;
|
|
StringRef attrs;
|
|
const Expression *origin = nullptr;
|
|
const Expression *length = nullptr;
|
|
|
|
name = _tok._range;
|
|
consumeToken();
|
|
|
|
// Parse optional memory region attributes.
|
|
if (_tok._kind == Token::l_paren) {
|
|
consumeToken();
|
|
|
|
if (_tok._kind != Token::identifier) {
|
|
error(_tok, "Expected memory attribute string.");
|
|
return nullptr;
|
|
}
|
|
attrs = _tok._range;
|
|
consumeToken();
|
|
|
|
if (!expectAndConsume(Token::r_paren, "expected )"))
|
|
return nullptr;
|
|
}
|
|
|
|
if (!expectAndConsume(Token::colon, "expected :"))
|
|
return nullptr;
|
|
|
|
// Parse the ORIGIN (base address of memory block).
|
|
if (!expectAndConsume(Token::kw_origin, "expected ORIGIN"))
|
|
return nullptr;
|
|
|
|
if (!expectAndConsume(Token::equal, "expected ="))
|
|
return nullptr;
|
|
|
|
origin = parseExpression();
|
|
if (!origin)
|
|
return nullptr;
|
|
|
|
if (!expectAndConsume(Token::comma, "expected ,"))
|
|
return nullptr;
|
|
|
|
// Parse the LENGTH (length of memory block).
|
|
if (!expectAndConsume(Token::kw_length, "expected LENGTH"))
|
|
return nullptr;
|
|
|
|
if (!expectAndConsume(Token::equal, "expected ="))
|
|
return nullptr;
|
|
|
|
length = parseExpression();
|
|
if (!length)
|
|
return nullptr;
|
|
|
|
MemoryBlock *block =
|
|
new (_alloc) MemoryBlock(name, attrs, origin, length);
|
|
blocks.push_back(block);
|
|
} else {
|
|
unrecognizedToken = true;
|
|
}
|
|
}
|
|
if (!expectAndConsume(
|
|
Token::r_brace,
|
|
"expected memory block definition."))
|
|
return nullptr;
|
|
|
|
return new (_alloc) Memory(*this, blocks);
|
|
}
|
|
|
|
Extern *Parser::parseExtern() {
|
|
assert(_tok._kind == Token::kw_extern && "Expected EXTERN!");
|
|
consumeToken();
|
|
if (!expectAndConsume(Token::l_paren, "expected ("))
|
|
return nullptr;
|
|
|
|
// Parse one or more symbols.
|
|
SmallVector<StringRef, 8> symbols;
|
|
if (_tok._kind != Token::identifier) {
|
|
error(_tok, "expected one or more symbols in EXTERN.");
|
|
return nullptr;
|
|
}
|
|
symbols.push_back(_tok._range);
|
|
consumeToken();
|
|
while (_tok._kind == Token::identifier) {
|
|
symbols.push_back(_tok._range);
|
|
consumeToken();
|
|
}
|
|
|
|
if (!expectAndConsume(Token::r_paren, "expected symbol in EXTERN."))
|
|
return nullptr;
|
|
|
|
return new (_alloc) Extern(*this, symbols);
|
|
}
|
|
|
|
// Sema member functions
|
|
Sema::Sema() : _parsedPHDRS(false) {}
|
|
|
|
void Sema::perform() {
|
|
for (auto &parser : _scripts)
|
|
perform(parser->get());
|
|
}
|
|
|
|
bool Sema::less(const SectionKey &lhs, const SectionKey &rhs) const {
|
|
int a = getLayoutOrder(lhs, true);
|
|
int b = getLayoutOrder(rhs, true);
|
|
|
|
if (a != b) {
|
|
if (a < 0)
|
|
return false;
|
|
if (b < 0)
|
|
return true;
|
|
return a < b;
|
|
}
|
|
|
|
// If both sections are not mapped anywhere, they have the same order
|
|
if (a < 0)
|
|
return false;
|
|
|
|
// If both sections fall into the same layout order, we need to find their
|
|
// relative position as written in the (InputSectionsCmd).
|
|
return localCompare(a, lhs, rhs);
|
|
}
|
|
|
|
StringRef Sema::getOutputSection(const SectionKey &key) const {
|
|
int layoutOrder = getLayoutOrder(key, true);
|
|
if (layoutOrder < 0)
|
|
return StringRef();
|
|
|
|
for (int i = layoutOrder - 1; i >= 0; --i) {
|
|
if (!isa<OutputSectionDescription>(_layoutCommands[i]))
|
|
continue;
|
|
|
|
const OutputSectionDescription *out =
|
|
dyn_cast<OutputSectionDescription>(_layoutCommands[i]);
|
|
return out->name();
|
|
}
|
|
|
|
return StringRef();
|
|
}
|
|
|
|
std::vector<const SymbolAssignment *>
|
|
Sema::getExprs(const SectionKey &key) {
|
|
int layoutOrder = getLayoutOrder(key, false);
|
|
auto ans = std::vector<const SymbolAssignment *>();
|
|
|
|
if (layoutOrder < 0 || _deliveredExprs.count(layoutOrder) > 0)
|
|
return ans;
|
|
|
|
for (int i = layoutOrder - 1; i >= 0; --i) {
|
|
if (isa<InputSection>(_layoutCommands[i]))
|
|
break;
|
|
if (auto assgn = dyn_cast<SymbolAssignment>(_layoutCommands[i]))
|
|
ans.push_back(assgn);
|
|
}
|
|
|
|
// Reverse this order so we evaluate the expressions in the original order
|
|
// of the linker script
|
|
std::reverse(ans.begin(), ans.end());
|
|
|
|
// Mark this layout number as delivered
|
|
_deliveredExprs.insert(layoutOrder);
|
|
return ans;
|
|
}
|
|
|
|
std::error_code Sema::evalExpr(const SymbolAssignment *assgn,
|
|
uint64_t &curPos) {
|
|
_symbolTable[StringRef(".")] = curPos;
|
|
|
|
auto ans = assgn->expr()->evalExpr(_symbolTable);
|
|
if (ans.getError())
|
|
return ans.getError();
|
|
uint64_t result = *ans;
|
|
|
|
if (assgn->symbol() == ".") {
|
|
curPos = result;
|
|
return std::error_code();
|
|
}
|
|
|
|
_symbolTable[assgn->symbol()] = result;
|
|
return std::error_code();
|
|
}
|
|
|
|
const llvm::StringSet<> &Sema::getScriptDefinedSymbols() const {
|
|
// Do we have cached results?
|
|
if (!_definedSymbols.empty())
|
|
return _definedSymbols;
|
|
|
|
// Populate our defined set and return it
|
|
for (auto cmd : _layoutCommands)
|
|
if (auto sa = dyn_cast<SymbolAssignment>(cmd)) {
|
|
StringRef symbol = sa->symbol();
|
|
if (!symbol.empty() && symbol != ".")
|
|
_definedSymbols.insert(symbol);
|
|
}
|
|
|
|
return _definedSymbols;
|
|
}
|
|
|
|
uint64_t Sema::getLinkerScriptExprValue(StringRef name) const {
|
|
auto it = _symbolTable.find(name);
|
|
assert (it != _symbolTable.end() && "Invalid symbol name!");
|
|
return it->second;
|
|
}
|
|
|
|
std::error_code
|
|
Sema::getPHDRsForOutputSection(StringRef name,
|
|
std::vector<const PHDR *> &phdrs) const {
|
|
// Cache results if not done yet.
|
|
if (auto ec = const_cast<Sema *>(this)->buildSectionToPHDR())
|
|
return ec;
|
|
|
|
auto vec = _sectionToPHDR.lookup(name);
|
|
std::copy(std::begin(vec), std::end(vec), std::back_inserter(phdrs));
|
|
return std::error_code();
|
|
}
|
|
|
|
void Sema::dump() const {
|
|
raw_ostream &os = llvm::outs();
|
|
os << "Linker script semantics dump\n";
|
|
int num = 0;
|
|
for (auto &parser : _scripts) {
|
|
os << "Dumping script #" << ++num << ":\n";
|
|
parser->get()->dump(os);
|
|
os << "\n";
|
|
}
|
|
os << "Dumping rule ids:\n";
|
|
for (unsigned i = 0; i < _layoutCommands.size(); ++i) {
|
|
os << "LayoutOrder " << i << ":\n";
|
|
_layoutCommands[i]->dump(os);
|
|
os << "\n\n";
|
|
}
|
|
}
|
|
|
|
/// Given a string "pattern" with wildcard characters, return true if it
|
|
/// matches "name". This function is useful when checking if a given name
|
|
/// pattern written in the linker script, i.e. ".text*", should match
|
|
/// ".text.anytext".
|
|
static bool wildcardMatch(StringRef pattern, StringRef name) {
|
|
auto i = name.begin();
|
|
|
|
// Check if each char in pattern also appears in our input name, handling
|
|
// special wildcard characters.
|
|
for (auto j = pattern.begin(), e = pattern.end(); j != e; ++j) {
|
|
if (i == name.end())
|
|
return false;
|
|
|
|
switch (*j) {
|
|
case '*':
|
|
while (!wildcardMatch(pattern.drop_front(j - pattern.begin() + 1),
|
|
name.drop_front(i - name.begin() + 1))) {
|
|
if (i == name.end())
|
|
return false;
|
|
++i;
|
|
}
|
|
break;
|
|
case '?':
|
|
// Matches any character
|
|
break;
|
|
case '[': {
|
|
// Matches a range of characters specified between brackets
|
|
size_t end = pattern.find(']', j - pattern.begin());
|
|
if (end == pattern.size())
|
|
return false;
|
|
|
|
StringRef chars = pattern.slice(j - pattern.begin(), end);
|
|
if (chars.find(i) == StringRef::npos)
|
|
return false;
|
|
|
|
j = pattern.begin() + end;
|
|
break;
|
|
}
|
|
case '\\':
|
|
++j;
|
|
if (*j != *i)
|
|
return false;
|
|
break;
|
|
default:
|
|
// No wildcard character means we must match exactly the same char
|
|
if (*j != *i)
|
|
return false;
|
|
break;
|
|
}
|
|
++i;
|
|
}
|
|
|
|
// If our pattern has't consumed the entire string, it is not a match
|
|
return i == name.end();
|
|
}
|
|
|
|
int Sema::matchSectionName(int id, const SectionKey &key) const {
|
|
const InputSectionsCmd *cmd = dyn_cast<InputSectionsCmd>(_layoutCommands[id]);
|
|
|
|
if (!cmd || !wildcardMatch(cmd->archiveName(), key.archivePath))
|
|
return -1;
|
|
|
|
while ((size_t)++id < _layoutCommands.size() &&
|
|
(isa<InputSection>(_layoutCommands[id]))) {
|
|
if (isa<InputSectionSortedGroup>(_layoutCommands[id]))
|
|
continue;
|
|
|
|
const InputSectionName *in =
|
|
dyn_cast<InputSectionName>(_layoutCommands[id]);
|
|
if (wildcardMatch(in->name(), key.sectionName))
|
|
return id;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int Sema::getLayoutOrder(const SectionKey &key, bool coarse) const {
|
|
// First check if we already answered this layout question
|
|
if (coarse) {
|
|
auto entry = _cacheSectionOrder.find(key);
|
|
if (entry != _cacheSectionOrder.end())
|
|
return entry->second;
|
|
} else {
|
|
auto entry = _cacheExpressionOrder.find(key);
|
|
if (entry != _cacheExpressionOrder.end())
|
|
return entry->second;
|
|
}
|
|
|
|
// Try to match exact file name
|
|
auto range = _memberToLayoutOrder.equal_range(key.memberPath);
|
|
for (auto I = range.first, E = range.second; I != E; ++I) {
|
|
int order = I->second;
|
|
int exprOrder = -1;
|
|
|
|
if ((exprOrder = matchSectionName(order, key)) >= 0) {
|
|
if (coarse) {
|
|
_cacheSectionOrder.insert(std::make_pair(key, order));
|
|
return order;
|
|
}
|
|
_cacheExpressionOrder.insert(std::make_pair(key, exprOrder));
|
|
return exprOrder;
|
|
}
|
|
}
|
|
|
|
// If we still couldn't find a rule for this input section, try to match
|
|
// wildcards
|
|
for (auto I = _memberNameWildcards.begin(), E = _memberNameWildcards.end();
|
|
I != E; ++I) {
|
|
if (!wildcardMatch(I->first, key.memberPath))
|
|
continue;
|
|
int order = I->second;
|
|
int exprOrder = -1;
|
|
|
|
if ((exprOrder = matchSectionName(order, key)) >= 0) {
|
|
if (coarse) {
|
|
_cacheSectionOrder.insert(std::make_pair(key, order));
|
|
return order;
|
|
}
|
|
_cacheExpressionOrder.insert(std::make_pair(key, exprOrder));
|
|
return exprOrder;
|
|
}
|
|
}
|
|
|
|
_cacheSectionOrder.insert(std::make_pair(key, -1));
|
|
_cacheExpressionOrder.insert(std::make_pair(key, -1));
|
|
return -1;
|
|
}
|
|
|
|
static bool compareSortedNames(WildcardSortMode sortMode, StringRef lhs,
|
|
StringRef rhs) {
|
|
switch (sortMode) {
|
|
case WildcardSortMode::None:
|
|
case WildcardSortMode::NA:
|
|
return false;
|
|
case WildcardSortMode::ByAlignment:
|
|
case WildcardSortMode::ByInitPriority:
|
|
case WildcardSortMode::ByAlignmentAndName:
|
|
assert(false && "Unimplemented sort order");
|
|
break;
|
|
case WildcardSortMode::ByName:
|
|
return lhs.compare(rhs) < 0;
|
|
case WildcardSortMode::ByNameAndAlignment:
|
|
int compare = lhs.compare(rhs);
|
|
if (compare != 0)
|
|
return compare < 0;
|
|
return compareSortedNames(WildcardSortMode::ByAlignment, lhs, rhs);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static bool sortedGroupContains(const InputSectionSortedGroup *cmd,
|
|
const Sema::SectionKey &key) {
|
|
for (const InputSection *child : *cmd) {
|
|
if (auto i = dyn_cast<InputSectionName>(child)) {
|
|
if (wildcardMatch(i->name(), key.sectionName))
|
|
return true;
|
|
continue;
|
|
}
|
|
|
|
auto *sortedGroup = dyn_cast<InputSectionSortedGroup>(child);
|
|
assert(sortedGroup && "Expected InputSectionSortedGroup object");
|
|
|
|
if (sortedGroupContains(sortedGroup, key))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool Sema::localCompare(int order, const SectionKey &lhs,
|
|
const SectionKey &rhs) const {
|
|
const InputSectionsCmd *cmd =
|
|
dyn_cast<InputSectionsCmd>(_layoutCommands[order]);
|
|
|
|
assert(cmd && "Invalid InputSectionsCmd index");
|
|
|
|
if (lhs.archivePath != rhs.archivePath)
|
|
return compareSortedNames(cmd->archiveSortMode(), lhs.archivePath,
|
|
rhs.archivePath);
|
|
|
|
if (lhs.memberPath != rhs.memberPath)
|
|
return compareSortedNames(cmd->fileSortMode(), lhs.memberPath,
|
|
rhs.memberPath);
|
|
|
|
// Both sections come from the same exact same file and rule. Start walking
|
|
// through input section names as written in the linker script and the
|
|
// first one to match will have higher priority.
|
|
for (const InputSection *inputSection : *cmd) {
|
|
if (auto i = dyn_cast<InputSectionName>(inputSection)) {
|
|
// If both match, return false (both have equal priority)
|
|
// If rhs match, return false (rhs has higher priority)
|
|
if (wildcardMatch(i->name(), rhs.sectionName))
|
|
return false;
|
|
// If lhs matches first, it has priority over rhs
|
|
if (wildcardMatch(i->name(), lhs.sectionName))
|
|
return true;
|
|
continue;
|
|
}
|
|
|
|
// Handle sorted subgroups specially
|
|
auto *sortedGroup = dyn_cast<InputSectionSortedGroup>(inputSection);
|
|
assert(sortedGroup && "Expected InputSectionSortedGroup object");
|
|
|
|
bool a = sortedGroupContains(sortedGroup, lhs);
|
|
bool b = sortedGroupContains(sortedGroup, rhs);
|
|
if (a && !b)
|
|
return false;
|
|
if (b && !a)
|
|
return true;
|
|
if (!a && !a)
|
|
continue;
|
|
|
|
return compareSortedNames(sortedGroup->sortMode(), lhs.sectionName,
|
|
rhs.sectionName);
|
|
}
|
|
|
|
llvm_unreachable("");
|
|
return false;
|
|
}
|
|
|
|
std::error_code Sema::buildSectionToPHDR() {
|
|
if (_parsedPHDRS)
|
|
return std::error_code();
|
|
_parsedPHDRS = true;
|
|
|
|
// No scripts - nothing to do.
|
|
if (_scripts.empty() || _layoutCommands.empty())
|
|
return std::error_code();
|
|
|
|
// Collect all header declarations.
|
|
llvm::StringMap<const PHDR *> phdrs;
|
|
for (auto &parser : _scripts) {
|
|
for (auto *cmd : parser->get()->_commands) {
|
|
if (auto *ph = dyn_cast<PHDRS>(cmd)) {
|
|
for (auto *p : *ph)
|
|
phdrs[p->name()] = p;
|
|
}
|
|
}
|
|
}
|
|
const bool noPhdrs = phdrs.empty();
|
|
|
|
// Add NONE header to the map provided there's no user-defined
|
|
// header with the same name.
|
|
if (!_sectionToPHDR.count(PHDR_NONE.name()))
|
|
phdrs[PHDR_NONE.name()] = &PHDR_NONE;
|
|
|
|
// Match output sections to available headers.
|
|
llvm::SmallVector<const PHDR *, 2> phdrsCur, phdrsLast { &PHDR_NONE };
|
|
for (const Command *cmd : _layoutCommands) {
|
|
auto osd = dyn_cast<OutputSectionDescription>(cmd);
|
|
if (!osd || osd->isDiscarded())
|
|
continue;
|
|
|
|
phdrsCur.clear();
|
|
for (StringRef name : osd->PHDRs()) {
|
|
auto it = phdrs.find(name);
|
|
if (it == phdrs.end()) {
|
|
return LinkerScriptReaderError::unknown_phdr_ids;
|
|
}
|
|
phdrsCur.push_back(it->second);
|
|
}
|
|
|
|
// If no headers and no errors - insert empty headers set.
|
|
// If the current set of headers is empty, then use the last non-empty
|
|
// set. Otherwise mark the current set to be the last non-empty set for
|
|
// successors.
|
|
if (noPhdrs)
|
|
phdrsCur.clear();
|
|
else if (phdrsCur.empty())
|
|
phdrsCur = phdrsLast;
|
|
else
|
|
phdrsLast = phdrsCur;
|
|
|
|
_sectionToPHDR[osd->name()] = phdrsCur;
|
|
}
|
|
return std::error_code();
|
|
}
|
|
|
|
static bool hasWildcard(StringRef name) {
|
|
for (auto ch : name)
|
|
if (ch == '*' || ch == '?' || ch == '[' || ch == '\\')
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
void Sema::linearizeAST(const InputSection *inputSection) {
|
|
if (isa<InputSectionName>(inputSection)) {
|
|
_layoutCommands.push_back(inputSection);
|
|
return;
|
|
}
|
|
|
|
auto *sortedGroup = dyn_cast<InputSectionSortedGroup>(inputSection);
|
|
assert(sortedGroup && "Expected InputSectionSortedGroup object");
|
|
|
|
for (const InputSection *child : *sortedGroup) {
|
|
linearizeAST(child);
|
|
}
|
|
}
|
|
|
|
void Sema::linearizeAST(const InputSectionsCmd *inputSections) {
|
|
StringRef memberName = inputSections->memberName();
|
|
// Populate our maps for fast lookup of InputSectionsCmd
|
|
if (hasWildcard(memberName))
|
|
_memberNameWildcards.push_back(
|
|
std::make_pair(memberName, (int)_layoutCommands.size()));
|
|
else if (!memberName.empty())
|
|
_memberToLayoutOrder.insert(
|
|
std::make_pair(memberName.str(), (int)_layoutCommands.size()));
|
|
|
|
_layoutCommands.push_back(inputSections);
|
|
for (const InputSection *inputSection : *inputSections)
|
|
linearizeAST(inputSection);
|
|
}
|
|
|
|
void Sema::linearizeAST(const Sections *sections) {
|
|
for (const Command *sectionCommand : *sections) {
|
|
if (isa<SymbolAssignment>(sectionCommand)) {
|
|
_layoutCommands.push_back(sectionCommand);
|
|
continue;
|
|
}
|
|
|
|
if (!isa<OutputSectionDescription>(sectionCommand))
|
|
continue;
|
|
|
|
_layoutCommands.push_back(sectionCommand);
|
|
auto *outSection = dyn_cast<OutputSectionDescription>(sectionCommand);
|
|
|
|
for (const Command *outSecCommand : *outSection) {
|
|
if (isa<SymbolAssignment>(outSecCommand)) {
|
|
_layoutCommands.push_back(outSecCommand);
|
|
continue;
|
|
}
|
|
|
|
if (!isa<InputSectionsCmd>(outSecCommand))
|
|
continue;
|
|
|
|
linearizeAST(dyn_cast<InputSectionsCmd>(outSecCommand));
|
|
}
|
|
}
|
|
}
|
|
|
|
void Sema::perform(const LinkerScript *ls) {
|
|
for (const Command *c : ls->_commands) {
|
|
if (const Sections *sec = dyn_cast<Sections>(c))
|
|
linearizeAST(sec);
|
|
}
|
|
}
|
|
|
|
} // End namespace script
|
|
} // end namespace lld
|