| //===- 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 |