Index: include/lld/ReaderWriter/ELFLinkingContext.h =================================================================== --- include/lld/ReaderWriter/ELFLinkingContext.h +++ include/lld/ReaderWriter/ELFLinkingContext.h @@ -10,6 +10,7 @@ #ifndef LLD_READER_WRITER_ELF_LINKER_CONTEXT_H #define LLD_READER_WRITER_ELF_LINKER_CONTEXT_H +#include "LinkerScript.h" #include "lld/Core/LinkingContext.h" #include "lld/Core/Pass.h" #include "lld/Core/PassManager.h" @@ -296,6 +297,11 @@ addSearchPath("=/usr/lib"); } + // We can parse several linker scripts via command line whose ASTs are stored + // in the current linking context via addLinkerScript(). + void addLinkerScript(std::unique_ptr script) { + _scripts.push_back(std::move(script)); + } private: ELFLinkingContext() LLVM_DELETED_FUNCTION; @@ -335,6 +341,7 @@ StringRefVector _rpathLinkList; std::map _absoluteSymbols; llvm::StringSet<> _dynamicallyExportedSymbols; + std::vector> _scripts; }; } // end namespace lld Index: include/lld/ReaderWriter/LinkerScript.h =================================================================== --- include/lld/ReaderWriter/LinkerScript.h +++ include/lld/ReaderWriter/LinkerScript.h @@ -739,11 +739,34 @@ /// https://sourceware.org/binutils/docs/ld/Scripts.html class Parser { public: - explicit Parser(Lexer &lex) : _lex(lex), _peekAvailable(false) {} + explicit Parser(std::unique_ptr mb) + : _lex(std::move(mb)), _peekAvailable(false), _parsed(false), + _hasError(false) {} + + /// Let's not allow copying of Parser class because it would be expensive + /// to update all the AST pointers to a new buffer. + Parser(const Parser &instance) LLVM_DELETED_FUNCTION; + + /// If not already parsed, parses the linker script and returns a reference + /// to the top level node of the linker script AST. If already parsed, simply + /// returns the cached AST. Returns nullptr if an error occurred during + /// parsing. + LinkerScript *getTopLevelNode() { + if (_parsed) { + if (_hasError) + return nullptr; + return &_script; + } + _parsed = true; + LinkerScript *res = parse(); + if (res == nullptr) + _hasError = true; + return res; + } +private: LinkerScript *parse(); -private: /// Advances to the next token, either asking the Lexer to lex the next token /// or obtaining it from the look ahead buffer. void consumeToken() { @@ -981,7 +1004,7 @@ // The top-level/entry-point linker script AST node LinkerScript _script; - Lexer &_lex; + Lexer _lex; // Current token being analyzed Token _tok; @@ -989,6 +1012,8 @@ // Annotate whether we buffered the next token to allow peeking bool _peekAvailable; Token _bufferedToken; + bool _parsed; + bool _hasError; }; } // end namespace script } // end namespace lld Index: lib/Driver/GnuLdDriver.cpp =================================================================== --- lib/Driver/GnuLdDriver.cpp +++ lib/Driver/GnuLdDriver.cpp @@ -274,9 +274,8 @@ raw_ostream &diag) { // Read the script file from disk and parse. StringRef path = mb->getBufferIdentifier(); - auto lexer = llvm::make_unique(std::move(mb)); - auto parser = llvm::make_unique(*lexer); - script::LinkerScript *script = parser->parse(); + auto parser = llvm::make_unique(std::move(mb)); + script::LinkerScript *script = parser->getTopLevelNode(); if (!script) return LinkerScriptReaderError::parse_error; // Evaluate script commands. @@ -292,6 +291,8 @@ if (auto *output = dyn_cast(c)) ctx.setOutputPath(output->getOutputFileName()); } + // Transfer ownership of the script to the linking context + ctx.addLinkerScript(std::move(parser)); return std::error_code(); } Index: utils/linker-script-test/linker-script-test.cpp =================================================================== --- utils/linker-script-test/linker-script-test.cpp +++ utils/linker-script-test/linker-script-test.cpp @@ -48,9 +48,8 @@ llvm::errs() << ec.message() << "\n"; return 1; } - Lexer l(std::move(mb.get())); - Parser p(l); - LinkerScript *ls = p.parse(); + Parser p(std::move(mb.get())); + LinkerScript *ls = p.getTopLevelNode(); if (ls) ls->dump(llvm::outs()); }