LinkerScript: Add parsing of the MEMORY command
This patch implements parsing of the GNU ld MEMORY command [1]. The command and the memory block definitions are parsed as specified (including the slightly strange "o" and "l" keywords). Evaluation will be added at a later point in time. [1] https://sourceware.org/binutils/docs-2.25/ld/MEMORY.html llvm-svn: 231928
This commit is contained in:
parent
c04b6f242c
commit
2ce1ea86d0
|
@ -77,6 +77,9 @@ public:
|
|||
kw_hidden,
|
||||
kw_input,
|
||||
kw_keep,
|
||||
kw_length,
|
||||
kw_memory,
|
||||
kw_origin,
|
||||
kw_provide,
|
||||
kw_provide_hidden,
|
||||
kw_only_if_ro,
|
||||
|
@ -154,6 +157,7 @@ public:
|
|||
Group,
|
||||
Input,
|
||||
InputSectionsCmd,
|
||||
Memory,
|
||||
Output,
|
||||
OutputArch,
|
||||
OutputFormat,
|
||||
|
@ -798,6 +802,46 @@ private:
|
|||
llvm::ArrayRef<const Command *> _sectionsCommands;
|
||||
};
|
||||
|
||||
/// Represents a single memory block definition in a MEMORY {} command.
|
||||
class MemoryBlock {
|
||||
public:
|
||||
MemoryBlock(StringRef name, StringRef attr,
|
||||
const Expression *origin, const Expression *length)
|
||||
: _name(name), _attr(attr), _origin(origin), _length(length) {}
|
||||
|
||||
void dump(raw_ostream &os) const;
|
||||
|
||||
private:
|
||||
StringRef _name;
|
||||
StringRef _attr;
|
||||
const Expression *_origin;
|
||||
const Expression *_length;
|
||||
};
|
||||
|
||||
/// Represents all the contents of the MEMORY {} command.
|
||||
class Memory : public Command {
|
||||
public:
|
||||
Memory(Parser &ctx,
|
||||
const SmallVectorImpl<const MemoryBlock *> &blocks)
|
||||
: Command(ctx, Kind::Memory) {
|
||||
size_t numBlocks = blocks.size();
|
||||
const MemoryBlock **blocksStart =
|
||||
getAllocator().Allocate<const MemoryBlock *>(numBlocks);
|
||||
std::copy(std::begin(blocks), std::end(blocks), blocksStart);
|
||||
_blocks = llvm::makeArrayRef(blocksStart, numBlocks);
|
||||
}
|
||||
|
||||
static bool classof(const Command *c) {
|
||||
return c->getKind() == Kind::Memory;
|
||||
}
|
||||
|
||||
void dump(raw_ostream &os) const override;
|
||||
|
||||
private:
|
||||
llvm::ArrayRef<const MemoryBlock *> _blocks;
|
||||
};
|
||||
|
||||
|
||||
/// Stores the parse tree of a linker script.
|
||||
class LinkerScript {
|
||||
public:
|
||||
|
@ -1066,6 +1110,17 @@ private:
|
|||
///
|
||||
Sections *parseSections();
|
||||
|
||||
/// Parse the MEMORY linker script command.
|
||||
/// Example:
|
||||
///
|
||||
/// MEMORY {
|
||||
/// ^~~~ parseMemory()
|
||||
/// ram (rwx) : ORIGIN = 0x20000000, LENGTH = 96K
|
||||
/// rom (rx) : ORIGIN = 0x0, LENGTH = 256K
|
||||
/// }
|
||||
///
|
||||
Memory *parseMemory();
|
||||
|
||||
private:
|
||||
// Owns the entire linker script AST nodes
|
||||
llvm::BumpPtrAllocator _alloc;
|
||||
|
|
|
@ -66,6 +66,9 @@ void Token::dump(raw_ostream &os) const {
|
|||
CASE(kw_hidden)
|
||||
CASE(kw_input)
|
||||
CASE(kw_keep)
|
||||
CASE(kw_length)
|
||||
CASE(kw_memory)
|
||||
CASE(kw_origin)
|
||||
CASE(kw_provide)
|
||||
CASE(kw_provide_hidden)
|
||||
CASE(kw_only_if_ro)
|
||||
|
@ -468,8 +471,15 @@ void Lexer::lex(Token &tok) {
|
|||
.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)
|
||||
|
@ -916,6 +926,32 @@ void Sections::dump(raw_ostream &os) const {
|
|||
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";
|
||||
}
|
||||
|
||||
// Parser functions
|
||||
std::error_code Parser::parse() {
|
||||
// Get the first token.
|
||||
|
@ -998,6 +1034,13 @@ std::error_code Parser::parse() {
|
|||
_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;
|
||||
}
|
||||
default:
|
||||
// Unexpected.
|
||||
error(_tok, "expected linker script command");
|
||||
|
@ -1975,5 +2018,82 @@ Sections *Parser::parseSections() {
|
|||
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);
|
||||
}
|
||||
|
||||
} // end namespace script
|
||||
} // end namespace lld
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
RUN: linker-script-test %s | FileCheck %s
|
||||
*/
|
||||
|
||||
MEMORY
|
||||
{
|
||||
}
|
||||
|
||||
/*
|
||||
CHECK: kw_memory: MEMORY
|
||||
CHECK: l_brace: {
|
||||
CHECK: r_brace: }
|
||||
CHECK: eof:
|
||||
CHECK: MEMORY
|
||||
CHECK: {
|
||||
CHECK: }
|
||||
*/
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
RUN: linker-script-test %s 2> %t | FileCheck %s
|
||||
RUN: FileCheck -input-file %t -check-prefix=CHECK-ERR %s
|
||||
*/
|
||||
|
||||
MEMORY
|
||||
{
|
||||
ram () : ORIGIN = 0x20000000, LENGTH = 128M
|
||||
/*
|
||||
CHECK-ERR: [[@LINE-2]]:8: error: Expected memory attribute string.
|
||||
CHECK-ERR-NEXT: {{^ ram \(\) : ORIGIN = 0x20000000, LENGTH = 128M}}
|
||||
CHECK-ERR-NEXT: {{^ \^}}
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
CHECK: kw_memory: MEMORY
|
||||
CHECK: l_brace: {
|
||||
CHECK: identifier: ram
|
||||
CHECK: l_paren: (
|
||||
CHECK: r_paren: )
|
||||
CHECK: colon: :
|
||||
CHECK: kw_origin: ORIGIN
|
||||
CHECK: equal: =
|
||||
CHECK: number: 0x20000000
|
||||
CHECK: comma: ,
|
||||
CHECK: kw_length: LENGTH
|
||||
CHECK: equal: =
|
||||
CHECK: number: 128M
|
||||
CHECK: r_brace: }
|
||||
CHECK: eof:
|
||||
*/
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
RUN: linker-script-test %s 2> %t | FileCheck %s
|
||||
RUN: FileCheck -input-file %t -check-prefix=CHECK-ERR %s
|
||||
*/
|
||||
|
||||
MEMORY
|
||||
{
|
||||
ram (rwx) : ORIGIN = 0x20000000,
|
||||
}
|
||||
/*
|
||||
CHECK-ERR: [[@LINE-2]]:1: error: expected LENGTH
|
||||
CHECK-ERR-NEXT: {{^}}}
|
||||
CHECK-ERR-NEXT: {{^\^}}
|
||||
*/
|
||||
|
||||
/*
|
||||
CHECK: kw_memory: MEMORY
|
||||
CHECK: l_brace: {
|
||||
CHECK: identifier: ram
|
||||
CHECK: l_paren: (
|
||||
CHECK: identifier: rwx
|
||||
CHECK: r_paren: )
|
||||
CHECK: colon: :
|
||||
CHECK: kw_origin: ORIGIN
|
||||
CHECK: equal: =
|
||||
CHECK: number: 0x20000000
|
||||
CHECK: r_brace: }
|
||||
CHECK: eof:
|
||||
*/
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
RUN: linker-script-test %s 2> %t | FileCheck %s
|
||||
RUN: FileCheck -input-file %t -check-prefix=CHECK-ERR %s
|
||||
*/
|
||||
|
||||
MEMORY
|
||||
{
|
||||
(rwx) : ORIGIN = 0x20000000, LENGTH = 128M
|
||||
/*
|
||||
CHECK-ERR: [[@LINE-2]]:3: error: expected memory block definition.
|
||||
CHECK-ERR-NEXT: {{^ \(rwx\) : ORIGIN = 0x20000000, LENGTH = 128M}}
|
||||
CHECK-ERR-NEXT: {{^ \^}}
|
||||
*/
|
||||
}
|
||||
|
||||
/*
|
||||
CHECK: kw_memory: MEMORY
|
||||
CHECK: l_brace: {
|
||||
CHECK: l_paren: (
|
||||
CHECK: r_paren: )
|
||||
CHECK: colon: :
|
||||
CHECK: kw_origin: ORIGIN
|
||||
CHECK: equal: =
|
||||
CHECK: number: 0x20000000
|
||||
CHECK: comma: ,
|
||||
CHECK: kw_length: LENGTH
|
||||
CHECK: equal: =
|
||||
CHECK: number: 128M
|
||||
CHECK: r_brace: }
|
||||
CHECK: eof:
|
||||
*/
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
RUN: linker-script-test %s 2> %t | FileCheck %s
|
||||
RUN: FileCheck -input-file %t -check-prefix=CHECK-ERR %s
|
||||
*/
|
||||
|
||||
MEMORY
|
||||
{
|
||||
ram (rwx) : LENGTH = 128M
|
||||
/*
|
||||
CHECK-ERR: [[@LINE-2]]:15: error: expected ORIGIN
|
||||
CHECK-ERR-NEXT: {{^ ram \(rwx\) : LENGTH = 128M}}
|
||||
CHECK-ERR-NEXT: {{^ \^}}
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
CHECK: kw_memory: MEMORY
|
||||
CHECK: l_brace: {
|
||||
CHECK: identifier: ram
|
||||
CHECK: l_paren: (
|
||||
CHECK: identifier: rwx
|
||||
CHECK: r_paren: )
|
||||
CHECK: colon: :
|
||||
CHECK: kw_length: LENGTH
|
||||
CHECK: equal: =
|
||||
CHECK: number: 128M
|
||||
CHECK: r_brace: }
|
||||
CHECK: eof:
|
||||
*/
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
RUN: linker-script-test %s | FileCheck %s
|
||||
*/
|
||||
|
||||
MEMORY
|
||||
{
|
||||
ram (rwx) : ORIGIN = 0x20000000, LENGTH = 96K
|
||||
rom (rx) : org = 0x0, len = 256K
|
||||
boot : o = 0x1000000, l = 0x5f00
|
||||
}
|
||||
|
||||
/*
|
||||
CHECK: kw_memory: MEMORY
|
||||
CHECK: l_brace: {
|
||||
CHECK: identifier: ram
|
||||
CHECK: l_paren: (
|
||||
CHECK: identifier: rwx
|
||||
CHECK: r_paren: )
|
||||
CHECK: colon: :
|
||||
CHECK: kw_origin: ORIGIN
|
||||
CHECK: equal: =
|
||||
CHECK: number: 0x20000000
|
||||
CHECK: comma: ,
|
||||
CHECK: kw_length: LENGTH
|
||||
CHECK: equal: =
|
||||
CHECK: number: 96K
|
||||
CHECK: identifier: rom
|
||||
CHECK: l_paren: (
|
||||
CHECK: identifier: rx
|
||||
CHECK: r_paren: )
|
||||
CHECK: colon: :
|
||||
CHECK: kw_origin: org
|
||||
CHECK: equal: =
|
||||
CHECK: number: 0x0
|
||||
CHECK: comma: ,
|
||||
CHECK: kw_length: len
|
||||
CHECK: equal: =
|
||||
CHECK: number: 256K
|
||||
CHECK: identifier: boot
|
||||
CHECK: colon: :
|
||||
CHECK: kw_origin: o
|
||||
CHECK: equal: =
|
||||
CHECK: number: 0x1000000
|
||||
CHECK: comma: ,
|
||||
CHECK: kw_length: l
|
||||
CHECK: equal: =
|
||||
CHECK: number: 0x5f00
|
||||
CHECK: r_brace: }
|
||||
CHECK: eof:
|
||||
CHECK: MEMORY
|
||||
CHECK: {
|
||||
CHECK: ram (rwx) : ORIGIN = 536870912, LENGTH = 98304
|
||||
CHECK: rom (rx) : ORIGIN = 0, LENGTH = 262144
|
||||
CHECK: boot : ORIGIN = 16777216, LENGTH = 24320
|
||||
CHECK: }
|
||||
*/
|
Loading…
Reference in New Issue