Index: include/lld/ReaderWriter/LinkerScript.h =================================================================== --- include/lld/ReaderWriter/LinkerScript.h +++ include/lld/ReaderWriter/LinkerScript.h @@ -73,6 +73,7 @@ kw_discard, kw_entry, kw_exclude_file, + kw_extern, kw_group, kw_hidden, kw_input, @@ -154,6 +155,7 @@ public: enum class Kind { Entry, + Extern, Group, Input, InputSectionsCmd, @@ -841,6 +843,29 @@ llvm::ArrayRef _blocks; }; +/// Represents an extern command. +class Extern : public Command { +public: + Extern(Parser &ctx, + const SmallVectorImpl &symbols) + : Command(ctx, Kind::Extern) { + size_t numSymbols = symbols.size(); + StringRef *symbolsStart = + getAllocator().Allocate(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 _symbols; +}; + /// Stores the parse tree of a linker script. class LinkerScript { @@ -1121,6 +1146,7 @@ /// Memory *parseMemory(); + Extern *parseExtern(); private: // Owns the entire linker script AST nodes llvm::BumpPtrAllocator _alloc; Index: lib/ReaderWriter/LinkerScript.cpp =================================================================== --- lib/ReaderWriter/LinkerScript.cpp +++ lib/ReaderWriter/LinkerScript.cpp @@ -62,6 +62,7 @@ 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 @@ .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 @@ 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 @@ _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 @@ 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 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 Index: test/LinkerScript/extern-bad-symbol.test =================================================================== --- /dev/null +++ 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: +*/ Index: test/LinkerScript/extern-empty.test =================================================================== --- /dev/null +++ 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: +*/ Index: test/LinkerScript/extern-valid.test =================================================================== --- /dev/null +++ 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) +*/