Skip to content

Commit

Permalink
LinkerScript: Add parsing of the EXTERN command
Browse files Browse the repository at this point in the history
This patch implements parsing of the GNU ld EXTERN command [1].
Evaluation will be added at a later point in time.

[1] https://sourceware.org/binutils/docs/ld/Miscellaneous-Commands.html#Miscellaneous-Commands

llvm-svn: 232110
  • Loading branch information
meadori committed Mar 12, 2015
1 parent 0a6da5d commit 748a71b
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 0 deletions.
32 changes: 32 additions & 0 deletions lld/include/lld/ReaderWriter/LinkerScript.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ class Token {
kw_discard,
kw_entry,
kw_exclude_file,
kw_extern,
kw_group,
kw_hidden,
kw_input,
Expand Down Expand Up @@ -154,6 +155,7 @@ class Command {
public:
enum class Kind {
Entry,
Extern,
Group,
Input,
InputSectionsCmd,
Expand Down Expand Up @@ -841,6 +843,28 @@ class Memory : public Command {
llvm::ArrayRef<const MemoryBlock *> _blocks;
};

/// Represents an extern command.
class Extern : public Command {
public:
Extern(Parser &ctx,
const SmallVectorImpl<StringRef> &symbols)
: Command(ctx, Kind::Extern) {
size_t numSymbols = symbols.size();
StringRef *symbolsStart =
getAllocator().Allocate<StringRef>(numSymbols);
std::copy(std::begin(symbols), std::end(symbols), symbolsStart);
_symbols = llvm::makeArrayRef(symbolsStart, numSymbols);
}

static bool classof(const Command *c) {
return c->getKind() == Kind::Extern;
}

void dump(raw_ostream &os) const override;

private:
llvm::ArrayRef<StringRef> _symbols;
};

/// Stores the parse tree of a linker script.
class LinkerScript {
Expand Down Expand Up @@ -1121,6 +1145,14 @@ class Parser {
///
Memory *parseMemory();

/// Parse the EXTERN linker script command.
/// Example:
///
/// EXTERN(symbol symbol ...)
/// ^~~~> parseExtern()
///
Extern *parseExtern();

private:
// Owns the entire linker script AST nodes
llvm::BumpPtrAllocator _alloc;
Expand Down
46 changes: 46 additions & 0 deletions lld/lib/ReaderWriter/LinkerScript.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ void Token::dump(raw_ostream &os) const {
CASE(kw_discard)
CASE(kw_entry)
CASE(kw_exclude_file)
CASE(kw_extern)
CASE(kw_group)
CASE(kw_hidden)
CASE(kw_input)
Expand Down Expand Up @@ -467,6 +468,7 @@ void Lexer::lex(Token &tok) {
.Case("AT", Token::kw_at)
.Case("ENTRY", Token::kw_entry)
.Case("EXCLUDE_FILE", Token::kw_exclude_file)
.Case("EXTERN", Token::kw_extern)
.Case("GROUP", Token::kw_group)
.Case("HIDDEN", Token::kw_hidden)
.Case("INPUT", Token::kw_input)
Expand Down Expand Up @@ -952,6 +954,18 @@ void Memory::dump(raw_ostream &os) const {
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.
Expand Down Expand Up @@ -1041,6 +1055,13 @@ std::error_code Parser::parse() {
_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");
Expand Down Expand Up @@ -2095,5 +2116,30 @@ Memory *Parser::parseMemory() {
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);
}

} // end namespace script
} // end namespace lld
22 changes: 22 additions & 0 deletions lld/test/LinkerScript/extern-bad-symbol.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
RUN: linker-script-test %s 2> %t | FileCheck %s
RUN: FileCheck -input-file %t -check-prefix=CHECK-ERR %s
*/


EXTERN(a b 3)
/*
CHECK-ERR: [[@LINE-2]]:12: error: expected symbol in EXTERN.
CHECK-ERR-NEXT: {{^EXTERN\(a b 3\)}}
CHECK-ERR-NEXT: {{^ \^}}
*/

/*
CHECK: kw_extern: EXTERN
CHECK: l_paren: (
CHECK: identifier: a
CHECK: identifier: b
CHECK: number: 3
CHECK: r_paren: )
CHECK: eof:
*/
19 changes: 19 additions & 0 deletions lld/test/LinkerScript/extern-empty.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
RUN: linker-script-test %s 2> %t | FileCheck %s
RUN: FileCheck -input-file %t -check-prefix=CHECK-ERR %s
*/


EXTERN()
/*
CHECK-ERR: [[@LINE-2]]:8: error: expected one or more symbols in EXTERN.
CHECK-ERR-NEXT: {{^EXTERN()}}
CHECK-ERR-NEXT: {{^ \^}}
*/

/*
CHECK: kw_extern: EXTERN
CHECK: l_paren: (
CHECK: r_paren: )
CHECK: eof:
*/
29 changes: 29 additions & 0 deletions lld/test/LinkerScript/extern-valid.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
RUN: linker-script-test %s | FileCheck %s
*/

EXTERN(a)
EXTERN(a b)
EXTERN(_foo _bar _baz)

/*
CHECK: kw_extern: EXTERN
CHECK: l_paren: (
CHECK: identifier: a
CHECK: r_paren: )
CHECK: kw_extern: EXTERN
CHECK: l_paren: (
CHECK: identifier: a
CHECK: identifier: b
CHECK: r_paren: )
CHECK: kw_extern: EXTERN
CHECK: l_paren: (
CHECK: identifier: _foo
CHECK: identifier: _bar
CHECK: identifier: _baz
CHECK: r_paren: )
CHECK: eof:
CHECK: EXTERN(a)
CHECK: EXTERN(a b)
CHECK: EXTERN(_foo _bar _baz)
*/

0 comments on commit 748a71b

Please sign in to comment.