diff options
Diffstat (limited to 'ELF/SymbolListFile.cpp')
| -rw-r--r-- | ELF/SymbolListFile.cpp | 168 |
1 files changed, 168 insertions, 0 deletions
diff --git a/ELF/SymbolListFile.cpp b/ELF/SymbolListFile.cpp new file mode 100644 index 0000000000000..9e088025c1b7f --- /dev/null +++ b/ELF/SymbolListFile.cpp @@ -0,0 +1,168 @@ +//===- SymbolListFile.cpp -------------------------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains the parser/evaluator of the linker script. +// It does not construct an AST but consume linker script directives directly. +// Results are written to Driver or Config object. +// +//===----------------------------------------------------------------------===// + +#include "SymbolListFile.h" +#include "Config.h" +#include "ScriptParser.h" +#include "llvm/Support/MemoryBuffer.h" + +using namespace llvm; +using namespace llvm::ELF; + +using namespace lld; +using namespace lld::elf; + +// Parse the --dynamic-list argument. A dynamic list is in the form +// +// { symbol1; symbol2; [...]; symbolN }; +// +// Multiple groups can be defined in the same file, and they are merged +// into a single group. + +class DynamicListParser final : public ScriptParserBase { +public: + DynamicListParser(StringRef S) : ScriptParserBase(S) {} + void run(); +}; + +void DynamicListParser::run() { + while (!atEOF()) { + expect("{"); + while (!Error) { + Config->DynamicList.push_back(next()); + expect(";"); + if (skip("}")) + break; + } + expect(";"); + } +} + +void elf::parseDynamicList(MemoryBufferRef MB) { + DynamicListParser(MB.getBuffer()).run(); +} + +// Parse the --version-script argument. We currently only accept the following +// version script syntax: +// +// { [ global: symbol1; symbol2; [...]; symbolN; ] local: *; }; +// +// No wildcards are supported, other than for the local entry. Symbol versioning +// is also not supported. + +class VersionScriptParser final : public ScriptParserBase { +public: + VersionScriptParser(StringRef S) : ScriptParserBase(S) {} + + void run(); + +private: + void parseExtern(std::vector<SymbolVersion> *Globals); + void parseVersion(StringRef VerStr); + void parseGlobal(StringRef VerStr); + void parseLocal(); +}; + +size_t elf::defineSymbolVersion(StringRef VerStr) { + // Identifiers start at 2 because 0 and 1 are reserved + // for VER_NDX_LOCAL and VER_NDX_GLOBAL constants. + size_t VersionId = Config->VersionDefinitions.size() + 2; + Config->VersionDefinitions.push_back({VerStr, VersionId}); + return VersionId; +} + +void VersionScriptParser::parseVersion(StringRef VerStr) { + defineSymbolVersion(VerStr); + + if (skip("global:") || peek() != "local:") + parseGlobal(VerStr); + if (skip("local:")) + parseLocal(); + expect("}"); + + // Each version may have a parent version. For example, "Ver2" defined as + // "Ver2 { global: foo; local: *; } Ver1;" has "Ver1" as a parent. This + // version hierarchy is, probably against your instinct, purely for human; the + // runtime doesn't care about them at all. In LLD, we simply skip the token. + if (!VerStr.empty() && peek() != ";") + next(); + expect(";"); +} + +void VersionScriptParser::parseLocal() { + Config->DefaultSymbolVersion = VER_NDX_LOCAL; + expect("*"); + expect(";"); +} + +void VersionScriptParser::parseExtern(std::vector<SymbolVersion> *Globals) { + expect("C++"); + expect("{"); + + for (;;) { + if (peek() == "}" || Error) + break; + Globals->push_back({next(), true}); + expect(";"); + } + + expect("}"); + expect(";"); +} + +void VersionScriptParser::parseGlobal(StringRef VerStr) { + std::vector<SymbolVersion> *Globals; + if (VerStr.empty()) + Globals = &Config->VersionScriptGlobals; + else + Globals = &Config->VersionDefinitions.back().Globals; + + for (;;) { + if (skip("extern")) + parseExtern(Globals); + + StringRef Cur = peek(); + if (Cur == "}" || Cur == "local:" || Error) + return; + next(); + Globals->push_back({Cur, false}); + expect(";"); + } +} + +void VersionScriptParser::run() { + StringRef Msg = "anonymous version definition is used in " + "combination with other version definitions"; + if (skip("{")) { + parseVersion(""); + if (!atEOF()) + setError(Msg); + return; + } + + while (!atEOF() && !Error) { + StringRef VerStr = next(); + if (VerStr == "{") { + setError(Msg); + return; + } + expect("{"); + parseVersion(VerStr); + } +} + +void elf::parseVersionScript(MemoryBufferRef MB) { + VersionScriptParser(MB.getBuffer()).run(); +} |
