diff options
Diffstat (limited to 'lib/Driver/WinLinkModuleDef.cpp')
-rw-r--r-- | lib/Driver/WinLinkModuleDef.cpp | 295 |
1 files changed, 295 insertions, 0 deletions
diff --git a/lib/Driver/WinLinkModuleDef.cpp b/lib/Driver/WinLinkModuleDef.cpp new file mode 100644 index 000000000000..e55a0bc5fe64 --- /dev/null +++ b/lib/Driver/WinLinkModuleDef.cpp @@ -0,0 +1,295 @@ +//===- lib/Driver/WinLinkModuleDef.cpp ------------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// \brief Windows module definition file parser. +/// +//===----------------------------------------------------------------------===// + +#include "lld/Driver/WinLinkModuleDef.h" +#include "llvm/ADT/StringSwitch.h" + +namespace lld { +namespace moduledef { + +Token Lexer::lex() { + for (;;) { + _buffer = _buffer.trim(); + if (_buffer.empty() || _buffer[0] == '\0') + return Token(Kind::eof, _buffer); + + switch (_buffer[0]) { + case ';': { + size_t end = _buffer.find('\n'); + _buffer = (end == _buffer.npos) ? "" : _buffer.drop_front(end); + continue; + } + case '=': + _buffer = _buffer.drop_front(); + return Token(Kind::equal, "="); + case ',': + _buffer = _buffer.drop_front(); + return Token(Kind::comma, ","); + case '"': { + size_t end = _buffer.find('"', 1); + Token ret; + if (end == _buffer.npos) { + ret = Token(Kind::identifier, _buffer.substr(1, end)); + _buffer = ""; + } else { + ret = Token(Kind::identifier, _buffer.substr(1, end - 1)); + _buffer = _buffer.drop_front(end + 1); + } + return ret; + } + default: { + size_t end = _buffer.find_first_not_of( + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789_.*~+!@#$%^&*()/"); + StringRef word = _buffer.substr(0, end); + Kind kind = llvm::StringSwitch<Kind>(word) + .Case("BASE", Kind::kw_base) + .Case("DATA", Kind::kw_data) + .Case("EXPORTS", Kind::kw_exports) + .Case("HEAPSIZE", Kind::kw_heapsize) + .Case("LIBRARY", Kind::kw_library) + .Case("NAME", Kind::kw_name) + .Case("NONAME", Kind::kw_noname) + .Case("PRIVATE", Kind::kw_private) + .Case("STACKSIZE", Kind::kw_stacksize) + .Case("VERSION", Kind::kw_version) + .Default(Kind::identifier); + _buffer = (end == _buffer.npos) ? "" : _buffer.drop_front(end); + return Token(kind, word); + } + } + } +} + +void Parser::consumeToken() { + if (_tokBuf.empty()) { + _tok = _lex.lex(); + return; + } + _tok = _tokBuf.back(); + _tokBuf.pop_back(); +} + +bool Parser::consumeTokenAsInt(uint64_t &result) { + consumeToken(); + if (_tok._kind != Kind::identifier) { + ungetToken(); + error(_tok, "Integer expected"); + return false; + } + if (_tok._range.getAsInteger(10, result)) { + error(_tok, "Integer expected"); + return false; + } + return true; +} + +bool Parser::expectAndConsume(Kind kind, Twine msg) { + consumeToken(); + if (_tok._kind != kind) { + error(_tok, msg); + return false; + } + return true; +} + +void Parser::ungetToken() { _tokBuf.push_back(_tok); } + +void Parser::error(const Token &tok, Twine msg) { + _lex.getSourceMgr().PrintMessage( + llvm::SMLoc::getFromPointer(tok._range.data()), llvm::SourceMgr::DK_Error, + msg); +} + +bool Parser::parse(std::vector<Directive *> &ret) { + for (;;) { + Directive *dir = nullptr; + if (!parseOne(dir)) + return false; + if (!dir) + return true; + ret.push_back(dir); + } +} + +bool Parser::parseOne(Directive *&ret) { + consumeToken(); + switch (_tok._kind) { + case Kind::eof: + return true; + case Kind::kw_exports: { + // EXPORTS + std::vector<PECOFFLinkingContext::ExportDesc> exports; + for (;;) { + PECOFFLinkingContext::ExportDesc desc; + if (!parseExport(desc)) + break; + exports.push_back(desc); + } + ret = new (_alloc) Exports(exports); + return true; + } + case Kind::kw_heapsize: { + // HEAPSIZE + uint64_t reserve, commit; + if (!parseMemorySize(reserve, commit)) + return false; + ret = new (_alloc) Heapsize(reserve, commit); + return true; + } + case Kind::kw_library: { + // LIBRARY + std::string name; + uint64_t baseaddr; + if (!parseName(name, baseaddr)) + return false; + if (!StringRef(name).endswith_lower(".dll")) + name.append(".dll"); + ret = new (_alloc) Library(name, baseaddr); + return true; + } + case Kind::kw_stacksize: { + // STACKSIZE + uint64_t reserve, commit; + if (!parseMemorySize(reserve, commit)) + return false; + ret = new (_alloc) Stacksize(reserve, commit); + return true; + } + case Kind::kw_name: { + // NAME + std::string outputPath; + uint64_t baseaddr; + if (!parseName(outputPath, baseaddr)) + return false; + ret = new (_alloc) Name(outputPath, baseaddr); + return true; + } + case Kind::kw_version: { + // VERSION + int major, minor; + if (!parseVersion(major, minor)) + return false; + ret = new (_alloc) Version(major, minor); + return true; + } + default: + error(_tok, Twine("Unknown directive: ") + _tok._range); + return false; + } +} + +bool Parser::parseExport(PECOFFLinkingContext::ExportDesc &result) { + consumeToken(); + if (_tok._kind != Kind::identifier) { + ungetToken(); + return false; + } + result.name = _tok._range; + + consumeToken(); + if (_tok._kind == Kind::equal) { + consumeToken(); + if (_tok._kind != Kind::identifier) + return false; + result.externalName = result.name; + result.name = _tok._range; + } else { + ungetToken(); + } + + for (;;) { + consumeToken(); + if (_tok._kind == Kind::identifier && _tok._range[0] == '@') { + _tok._range.drop_front().getAsInteger(10, result.ordinal); + consumeToken(); + if (_tok._kind == Kind::kw_noname) { + result.noname = true; + } else { + ungetToken(); + } + continue; + } + if (_tok._kind == Kind::kw_data) { + result.isData = true; + continue; + } + if (_tok._kind == Kind::kw_private) { + result.isPrivate = true; + continue; + } + ungetToken(); + return true; + } +} + +// HEAPSIZE/STACKSIZE reserve[,commit] +bool Parser::parseMemorySize(uint64_t &reserve, uint64_t &commit) { + if (!consumeTokenAsInt(reserve)) + return false; + + consumeToken(); + if (_tok._kind != Kind::comma) { + ungetToken(); + commit = 0; + return true; + } + + if (!consumeTokenAsInt(commit)) + return false; + return true; +} + +// NAME [outputPath] [BASE=address] +bool Parser::parseName(std::string &outputPath, uint64_t &baseaddr) { + consumeToken(); + if (_tok._kind == Kind::identifier) { + outputPath = _tok._range; + } else { + outputPath = ""; + ungetToken(); + return true; + } + consumeToken(); + if (_tok._kind == Kind::kw_base) { + if (!expectAndConsume(Kind::equal, "'=' expected")) + return false; + if (!consumeTokenAsInt(baseaddr)) + return false; + } else { + ungetToken(); + baseaddr = 0; + } + return true; +} + +// VERSION major[.minor] +bool Parser::parseVersion(int &major, int &minor) { + consumeToken(); + if (_tok._kind != Kind::identifier) + return false; + StringRef v1, v2; + std::tie(v1, v2) = _tok._range.split('.'); + if (v1.getAsInteger(10, major)) + return false; + if (v2.empty()) { + minor = 0; + } else if (v2.getAsInteger(10, minor)) { + return false; + } + return true; +} + +} // moddef +} // namespace lld |