summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lld/include/lld/ReaderWriter/LinkerScript.h32
-rw-r--r--lld/lib/ReaderWriter/LinkerScript.cpp46
-rw-r--r--lld/test/LinkerScript/extern-bad-symbol.test22
-rw-r--r--lld/test/LinkerScript/extern-empty.test19
-rw-r--r--lld/test/LinkerScript/extern-valid.test29
5 files changed, 148 insertions, 0 deletions
diff --git a/lld/include/lld/ReaderWriter/LinkerScript.h b/lld/include/lld/ReaderWriter/LinkerScript.h
index ada267cb84f..5c74ae1d3f1 100644
--- a/lld/include/lld/ReaderWriter/LinkerScript.h
+++ b/lld/include/lld/ReaderWriter/LinkerScript.h
@@ -73,6 +73,7 @@ public:
kw_discard,
kw_entry,
kw_exclude_file,
+ kw_extern,
kw_group,
kw_hidden,
kw_input,
@@ -154,6 +155,7 @@ class Command {
public:
enum class Kind {
Entry,
+ Extern,
Group,
Input,
InputSectionsCmd,
@@ -841,6 +843,28 @@ private:
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 {
@@ -1121,6 +1145,14 @@ private:
///
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;
diff --git a/lld/lib/ReaderWriter/LinkerScript.cpp b/lld/lib/ReaderWriter/LinkerScript.cpp
index 26ed59549d0..e264c5c0d2a 100644
--- a/lld/lib/ReaderWriter/LinkerScript.cpp
+++ b/lld/lib/ReaderWriter/LinkerScript.cpp
@@ -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)
@@ -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)
@@ -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.
@@ -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");
@@ -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
diff --git a/lld/test/LinkerScript/extern-bad-symbol.test b/lld/test/LinkerScript/extern-bad-symbol.test
new file mode 100644
index 00000000000..279a7cc2056
--- /dev/null
+++ b/lld/test/LinkerScript/extern-bad-symbol.test
@@ -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:
+*/
diff --git a/lld/test/LinkerScript/extern-empty.test b/lld/test/LinkerScript/extern-empty.test
new file mode 100644
index 00000000000..a5e1ece084d
--- /dev/null
+++ b/lld/test/LinkerScript/extern-empty.test
@@ -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:
+*/
diff --git a/lld/test/LinkerScript/extern-valid.test b/lld/test/LinkerScript/extern-valid.test
new file mode 100644
index 00000000000..764b4668a34
--- /dev/null
+++ b/lld/test/LinkerScript/extern-valid.test
@@ -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)
+*/
OpenPOWER on IntegriCloud