Newer
Older
//===- lib/Driver/WinLinkDriver.cpp ---------------------------------------===//
//
// The LLVM Linker
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
///
/// \file
///
/// Concrete instance of the Driver for Windows link.exe.
///
//===----------------------------------------------------------------------===//
#include <algorithm>
#include "lld/Driver/Driver.h"
#include "lld/Driver/WinLinkInputGraph.h"
#include "lld/ReaderWriter/PECOFFLinkingContext.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/Optional.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/Option.h"
#include "llvm/Support/Process.h"
#include "llvm/Support/Program.h"
#include "llvm/Support/raw_ostream.h"
namespace lld {
namespace {
//
// Option definitions
//
// Create enum with OPT_xxx values for each option in WinLinkOptions.td
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELP, META) \
OPT_##ID,
#include "WinLinkOptions.inc"
#undef OPTION
};
// Create prefix string literals used in WinLinkOptions.td
#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
#include "WinLinkOptions.inc"
#undef PREFIX
// Create table mapping all options defined in WinLinkOptions.td
static const llvm::opt::OptTable::Info infoTable[] = {
#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
HELPTEXT, METAVAR) \
{ PREFIX, NAME, HELPTEXT, METAVAR, OPT_##ID, llvm::opt::Option::KIND##Class, \
PARAM, FLAGS, OPT_##GROUP, OPT_##ALIAS, ALIASARGS },
#include "WinLinkOptions.inc"
#undef OPTION
};
// Create OptTable class for parsing actual command line arguments
class WinLinkOptTable : public llvm::opt::OptTable {
public:
Rui Ueyama
committed
// link.exe's command line options are case insensitive, unlike
// other driver's options for Unix.
WinLinkOptTable()
: OptTable(infoTable, llvm::array_lengthof(infoTable),
/* ignoreCase */ true) {}
//
// Functions to parse each command line option
//
// Split the given string with spaces.
std::vector<std::string> splitArgList(const std::string &str) {
std::stringstream stream(str);
std::istream_iterator<std::string> begin(stream);
std::istream_iterator<std::string> end;
return std::vector<std::string>(begin, end);
}
// Split the given string with the path separator.
std::vector<StringRef> splitPathList(StringRef str) {
std::vector<StringRef> ret;
while (!str.empty()) {
StringRef path;
llvm::tie(path, str) = str.split(';');
ret.push_back(path);
}
// Parse an argument for /base, /stack or /heap. The expected string
// is "<integer>[,<integer>]".
bool parseMemoryOption(StringRef arg, uint64_t &reserve, uint64_t &commit) {
StringRef reserveStr, commitStr;
llvm::tie(reserveStr, commitStr) = arg.split(',');
if (reserveStr.getAsInteger(0, reserve))
if (!commitStr.empty() && commitStr.getAsInteger(0, commit))
return false;
return true;
// Parse an argument for /version or /subsystem. The expected string is
// "<integer>[.<integer>]".
bool parseVersion(StringRef arg, uint32_t &major, uint32_t &minor) {
StringRef majorVersion, minorVersion;
llvm::tie(majorVersion, minorVersion) = arg.split('.');
if (minorVersion.empty())
minorVersion = "0";
if (majorVersion.getAsInteger(0, major))
if (minorVersion.getAsInteger(0, minor))
return false;
return true;
// Returns subsystem type for the given string.
llvm::COFF::WindowsSubsystem stringToWinSubsystem(StringRef str) {
return llvm::StringSwitch<llvm::COFF::WindowsSubsystem>(str.lower())
.Case("windows", llvm::COFF::IMAGE_SUBSYSTEM_WINDOWS_GUI)
.Case("console", llvm::COFF::IMAGE_SUBSYSTEM_WINDOWS_CUI)
.Case("boot_application",
llvm::COFF::IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION)
.Case("efi_application", llvm::COFF::IMAGE_SUBSYSTEM_EFI_APPLICATION)
.Case("efi_boot_service_driver",
llvm::COFF::IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER)
.Case("efi_rom", llvm::COFF::IMAGE_SUBSYSTEM_EFI_ROM)
.Case("efi_runtime_driver",
llvm::COFF::IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER)
.Case("native", llvm::COFF::IMAGE_SUBSYSTEM_NATIVE)
.Case("posix", llvm::COFF::IMAGE_SUBSYSTEM_POSIX_CUI)
.Default(llvm::COFF::IMAGE_SUBSYSTEM_UNKNOWN);
}
// Parse /subsystem command line option. The form of /subsystem is
// "subsystem_name[,majorOSVersion[.minorOSVersion]]".
bool parseSubsystem(StringRef arg, llvm::COFF::WindowsSubsystem &subsystem,
llvm::Optional<uint32_t> &major,
llvm::Optional<uint32_t> &minor, raw_ostream &diagnostics) {
StringRef subsystemStr, osVersion;
llvm::tie(subsystemStr, osVersion) = arg.split(',');
if (!osVersion.empty()) {
uint32_t v1, v2;
if (!parseVersion(osVersion, v1, v2))
return false;
major = v1;
minor = v2;
}
subsystem = stringToWinSubsystem(subsystemStr);
if (subsystem == llvm::COFF::IMAGE_SUBSYSTEM_UNKNOWN) {
diagnostics << "error: unknown subsystem name: " << subsystemStr << "\n";
return false;
}
return true;
}
llvm::COFF::MachineTypes stringToMachineType(StringRef str) {
return llvm::StringSwitch<llvm::COFF::MachineTypes>(str.lower())
.Case("arm", llvm::COFF::IMAGE_FILE_MACHINE_ARM)
.Case("ebc", llvm::COFF::IMAGE_FILE_MACHINE_EBC)
.Case("x64", llvm::COFF::IMAGE_FILE_MACHINE_AMD64)
.Case("x86", llvm::COFF::IMAGE_FILE_MACHINE_I386)
.Default(llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN);
}
// Parse /section:name,[[!]{DEKPRSW}]
//
// /section option is to set non-default bits in the Characteristics fields of
// the section header. D, E, K, P, R, S, and W represent discardable,
// execute, not_cachable, not_pageable, read, shared, and write bits,
// respectively. You can specify multiple flags in one /section option.
//
// If the flag starts with "!", the flags represent a mask that should be turned
// off regardless of the default value. You can even create a section which is
// not readable, writable nor executable with this -- although it's probably
// useless.
bool parseSection(StringRef option, std::string §ion,
llvm::Optional<uint32_t> &flags,
llvm::Optional<uint32_t> &mask) {
StringRef flagString;
llvm::tie(section, flagString) = option.split(",");
bool negative = false;
if (flagString.startswith("!")) {
negative = true;
flagString = flagString.substr(1);
}
if (flagString.empty())
return false;
Loading
Loading full blame...