There are two ways to specify a symbol to be exported in the module definition file. 1) EXPORT <external name> = <symbol> 2) EXPORT <symbol> In (1), you give both external name and internal name. In that case, the linker tries to find a symbol using the internal name, and write that address to the export table with the external name. Thus, from the outer world, the symbol seems to be exported as the external name. In (2), internal name is basically the same as the external name with an exception: if you give an undecorated symbol to the EXPORT directive, and if the linker finds a decorated symbol, the external name for the symbol will become the decorated symbol. LLD didn't implement that exception correctly. This patch fixes that. llvm-svn: 220333
296 lines
7.2 KiB
C++
296 lines
7.2 KiB
C++
//===- 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
|