diff options
Diffstat (limited to 'lib/Driver')
-rw-r--r-- | lib/Driver/CMakeLists.txt | 43 | ||||
-rw-r--r-- | lib/Driver/CoreDriver.cpp | 172 | ||||
-rw-r--r-- | lib/Driver/CoreOptions.td | 15 | ||||
-rw-r--r-- | lib/Driver/DarwinLdDriver.cpp | 832 | ||||
-rw-r--r-- | lib/Driver/DarwinLdOptions.td | 187 | ||||
-rw-r--r-- | lib/Driver/Driver.cpp | 130 | ||||
-rw-r--r-- | lib/Driver/GnuLdDriver.cpp | 760 | ||||
-rw-r--r-- | lib/Driver/GnuLdOptions.td | 323 | ||||
-rw-r--r-- | lib/Driver/Makefile | 38 | ||||
-rw-r--r-- | lib/Driver/TODO.rst | 101 | ||||
-rw-r--r-- | lib/Driver/UniversalDriver.cpp | 218 | ||||
-rw-r--r-- | lib/Driver/UniversalDriverOptions.td | 19 | ||||
-rw-r--r-- | lib/Driver/WinLinkDriver.cpp | 1371 | ||||
-rw-r--r-- | lib/Driver/WinLinkModuleDef.cpp | 295 | ||||
-rw-r--r-- | lib/Driver/WinLinkOptions.td | 120 |
15 files changed, 4624 insertions, 0 deletions
diff --git a/lib/Driver/CMakeLists.txt b/lib/Driver/CMakeLists.txt new file mode 100644 index 0000000000000..5a410e7eed7e9 --- /dev/null +++ b/lib/Driver/CMakeLists.txt @@ -0,0 +1,43 @@ +set(LLVM_TARGET_DEFINITIONS UniversalDriverOptions.td) +tablegen(LLVM UniversalDriverOptions.inc -gen-opt-parser-defs) +set(LLVM_TARGET_DEFINITIONS GnuLdOptions.td) +tablegen(LLVM GnuLdOptions.inc -gen-opt-parser-defs) +set(LLVM_TARGET_DEFINITIONS CoreOptions.td) +tablegen(LLVM CoreOptions.inc -gen-opt-parser-defs) +set(LLVM_TARGET_DEFINITIONS DarwinLdOptions.td) +tablegen(LLVM DarwinLdOptions.inc -gen-opt-parser-defs) +set(LLVM_TARGET_DEFINITIONS WinLinkOptions.td) +tablegen(LLVM WinLinkOptions.inc -gen-opt-parser-defs) +add_public_tablegen_target(DriverOptionsTableGen) + +add_llvm_library(lldDriver + CoreDriver.cpp + DarwinLdDriver.cpp + Driver.cpp + GnuLdDriver.cpp + UniversalDriver.cpp + WinLinkDriver.cpp + WinLinkModuleDef.cpp + LINK_LIBS + lldConfig + lldMachO + lldPECOFF + lldELF + lldAArch64ELFTarget + lldARMELFTarget + lldHexagonELFTarget + lldMipsELFTarget + lldX86ELFTarget + lldExampleSubTarget + lldX86_64ELFTarget + lldCore + lldNative + lldReaderWriter + lldYAML + LLVMObject + LLVMOption + LLVMSupport + ) + +add_dependencies(lldDriver DriverOptionsTableGen) + diff --git a/lib/Driver/CoreDriver.cpp b/lib/Driver/CoreDriver.cpp new file mode 100644 index 0000000000000..b8adee55746f4 --- /dev/null +++ b/lib/Driver/CoreDriver.cpp @@ -0,0 +1,172 @@ +//===- lib/Driver/CoreDriver.cpp ------------------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lld/Core/Reader.h" +#include "lld/Driver/Driver.h" +#include "lld/ReaderWriter/CoreLinkingContext.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/Option.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/raw_ostream.h" + +using namespace lld; + +namespace { + +// Create enum with OPT_xxx values for each option in CoreOptions.td +enum { + OPT_INVALID = 0, +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELP, META) \ + OPT_##ID, +#include "CoreOptions.inc" +#undef OPTION +}; + +// Create prefix string literals used in CoreOptions.td +#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; +#include "CoreOptions.inc" +#undef PREFIX + +// Create table mapping all options defined in CoreOptions.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 "CoreOptions.inc" +#undef OPTION +}; + +// Create OptTable class for parsing actual command line arguments +class CoreOptTable : public llvm::opt::OptTable { +public: + CoreOptTable() : OptTable(infoTable, llvm::array_lengthof(infoTable)){} +}; + +} // namespace anonymous + + +namespace lld { + +static const Registry::KindStrings coreKindStrings[] = { + { CoreLinkingContext::TEST_RELOC_CALL32, "call32" }, + { CoreLinkingContext::TEST_RELOC_PCREL32, "pcrel32" }, + { CoreLinkingContext::TEST_RELOC_GOT_LOAD32, "gotLoad32" }, + { CoreLinkingContext::TEST_RELOC_GOT_USE32, "gotUse32" }, + { CoreLinkingContext::TEST_RELOC_LEA32_WAS_GOT, "lea32wasGot" }, + LLD_KIND_STRING_END +}; + +bool CoreDriver::link(int argc, const char *argv[], raw_ostream &diagnostics) { + CoreLinkingContext ctx; + + // Register possible input file parsers. + ctx.registry().addSupportNativeObjects(); + ctx.registry().addSupportYamlFiles(); + ctx.registry().addKindTable(Reference::KindNamespace::testing, + Reference::KindArch::all, coreKindStrings); + + if (!parse(argc, argv, ctx)) + return false; + return Driver::link(ctx); +} + +bool CoreDriver::parse(int argc, const char *argv[], CoreLinkingContext &ctx, + raw_ostream &diagnostics) { + // Parse command line options using CoreOptions.td + std::unique_ptr<llvm::opt::InputArgList> parsedArgs; + CoreOptTable table; + unsigned missingIndex; + unsigned missingCount; + parsedArgs.reset( + table.ParseArgs(&argv[1], &argv[argc], missingIndex, missingCount)); + if (missingCount) { + diagnostics << "error: missing arg value for '" + << parsedArgs->getArgString(missingIndex) << "' expected " + << missingCount << " argument(s).\n"; + return false; + } + + // Set default options + ctx.setOutputPath("-"); + ctx.setDeadStripping(false); + ctx.setGlobalsAreDeadStripRoots(false); + ctx.setPrintRemainingUndefines(false); + ctx.setAllowRemainingUndefines(true); + ctx.setSearchArchivesToOverrideTentativeDefinitions(false); + + // Process all the arguments and create input files. + for (auto inputArg : *parsedArgs) { + switch (inputArg->getOption().getID()) { + case OPT_mllvm: + ctx.appendLLVMOption(inputArg->getValue()); + break; + + case OPT_entry: + ctx.setEntrySymbolName(inputArg->getValue()); + break; + + case OPT_output: + ctx.setOutputPath(inputArg->getValue()); + break; + + case OPT_dead_strip: + ctx.setDeadStripping(true); + break; + + case OPT_keep_globals: + ctx.setGlobalsAreDeadStripRoots(true); + break; + + case OPT_undefines_are_errors: + ctx.setPrintRemainingUndefines(true); + ctx.setAllowRemainingUndefines(false); + break; + + case OPT_commons_search_archives: + ctx.setSearchArchivesToOverrideTentativeDefinitions(true); + break; + + case OPT_add_pass: + ctx.addPassNamed(inputArg->getValue()); + break; + + case OPT_INPUT: { + std::vector<std::unique_ptr<File>> files + = loadFile(ctx, inputArg->getValue(), false); + for (std::unique_ptr<File> &file : files) + ctx.getNodes().push_back(llvm::make_unique<FileNode>(std::move(file))); + break; + } + + default: + break; + } + } + + if (ctx.getNodes().empty()) { + diagnostics << "No input files\n"; + return false; + } + + // Validate the combination of options used. + return ctx.validate(diagnostics); +} + +} // namespace lld diff --git a/lib/Driver/CoreOptions.td b/lib/Driver/CoreOptions.td new file mode 100644 index 0000000000000..df7cb41737d23 --- /dev/null +++ b/lib/Driver/CoreOptions.td @@ -0,0 +1,15 @@ +include "llvm/Option/OptParser.td" + +def output : Separate<["-"], "o">; +def entry : Separate<["-"], "e">; + +def dead_strip : Flag<["--"], "dead-strip">; +def undefines_are_errors : Flag<["--"], "undefines-are-errors">; +def keep_globals : Flag<["--"], "keep-globals">; +def commons_search_archives : Flag<["--"], "commons-search-archives">; + +def add_pass : Separate<["--"], "add-pass">; + +def target : Separate<["-"], "target">, HelpText<"Target triple to link for">; +def mllvm : Separate<["-"], "mllvm">, HelpText<"Options to pass to LLVM">; + diff --git a/lib/Driver/DarwinLdDriver.cpp b/lib/Driver/DarwinLdDriver.cpp new file mode 100644 index 0000000000000..2c64aeee38a58 --- /dev/null +++ b/lib/Driver/DarwinLdDriver.cpp @@ -0,0 +1,832 @@ +//===- lib/Driver/DarwinLdDriver.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 darwin's ld. +/// +//===----------------------------------------------------------------------===// + +#include "lld/Core/File.h" +#include "lld/Core/ArchiveLibraryFile.h" +#include "lld/Core/SharedLibraryFile.h" +#include "lld/Driver/Driver.h" +#include "lld/ReaderWriter/MachOLinkingContext.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/Option.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Format.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/MachO.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> + +using namespace lld; + +namespace { + +// Create enum with OPT_xxx values for each option in DarwinLdOptions.td +enum { + OPT_INVALID = 0, +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELP, META) \ + OPT_##ID, +#include "DarwinLdOptions.inc" +#undef OPTION +}; + +// Create prefix string literals used in DarwinLdOptions.td +#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; +#include "DarwinLdOptions.inc" +#undef PREFIX + +// Create table mapping all options defined in DarwinLdOptions.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 "DarwinLdOptions.inc" +#undef OPTION +}; + +// Create OptTable class for parsing actual command line arguments +class DarwinLdOptTable : public llvm::opt::OptTable { +public: + DarwinLdOptTable() : OptTable(infoTable, llvm::array_lengthof(infoTable)){} +}; + +std::vector<std::unique_ptr<File>> +loadFile(MachOLinkingContext &ctx, StringRef path, + raw_ostream &diag, bool wholeArchive, bool upwardDylib) { + if (ctx.logInputFiles()) + diag << path << "\n"; + + ErrorOr<std::unique_ptr<MemoryBuffer>> mbOrErr = ctx.getMemoryBuffer(path); + if (std::error_code ec = mbOrErr.getError()) + return makeErrorFile(path, ec); + std::vector<std::unique_ptr<File>> files; + if (std::error_code ec = ctx.registry().loadFile(std::move(mbOrErr.get()), files)) + return makeErrorFile(path, ec); + for (std::unique_ptr<File> &pf : files) { + // If file is a dylib, inform LinkingContext about it. + if (SharedLibraryFile *shl = dyn_cast<SharedLibraryFile>(pf.get())) { + if (std::error_code ec = shl->parse()) + return makeErrorFile(path, ec); + ctx.registerDylib(reinterpret_cast<mach_o::MachODylibFile*>(shl), + upwardDylib); + } + } + if (wholeArchive) + return parseMemberFiles(files); + return files; +} + +} // anonymous namespace + +// Test may be running on Windows. Canonicalize the path +// separator to '/' to get consistent outputs for tests. +static std::string canonicalizePath(StringRef path) { + char sep = llvm::sys::path::get_separator().front(); + if (sep != '/') { + std::string fixedPath = path; + std::replace(fixedPath.begin(), fixedPath.end(), sep, '/'); + return fixedPath; + } else { + return path; + } +} + +static void addFile(StringRef path, MachOLinkingContext &ctx, + bool loadWholeArchive, + bool upwardDylib, raw_ostream &diag) { + std::vector<std::unique_ptr<File>> files = + loadFile(ctx, path, diag, loadWholeArchive, upwardDylib); + for (std::unique_ptr<File> &file : files) + ctx.getNodes().push_back(llvm::make_unique<FileNode>(std::move(file))); +} + +// Export lists are one symbol per line. Blank lines are ignored. +// Trailing comments start with #. +static std::error_code parseExportsList(StringRef exportFilePath, + MachOLinkingContext &ctx, + raw_ostream &diagnostics) { + // Map in export list file. + ErrorOr<std::unique_ptr<MemoryBuffer>> mb = + MemoryBuffer::getFileOrSTDIN(exportFilePath); + if (std::error_code ec = mb.getError()) + return ec; + ctx.addInputFileDependency(exportFilePath); + StringRef buffer = mb->get()->getBuffer(); + while (!buffer.empty()) { + // Split off each line in the file. + std::pair<StringRef, StringRef> lineAndRest = buffer.split('\n'); + StringRef line = lineAndRest.first; + // Ignore trailing # comments. + std::pair<StringRef, StringRef> symAndComment = line.split('#'); + StringRef sym = symAndComment.first.trim(); + if (!sym.empty()) + ctx.addExportSymbol(sym); + buffer = lineAndRest.second; + } + return std::error_code(); +} + + + +/// Order files are one symbol per line. Blank lines are ignored. +/// Trailing comments start with #. Symbol names can be prefixed with an +/// architecture name and/or .o leaf name. Examples: +/// _foo +/// bar.o:_bar +/// libfrob.a(bar.o):_bar +/// x86_64:_foo64 +static std::error_code parseOrderFile(StringRef orderFilePath, + MachOLinkingContext &ctx, + raw_ostream &diagnostics) { + // Map in order file. + ErrorOr<std::unique_ptr<MemoryBuffer>> mb = + MemoryBuffer::getFileOrSTDIN(orderFilePath); + if (std::error_code ec = mb.getError()) + return ec; + ctx.addInputFileDependency(orderFilePath); + StringRef buffer = mb->get()->getBuffer(); + while (!buffer.empty()) { + // Split off each line in the file. + std::pair<StringRef, StringRef> lineAndRest = buffer.split('\n'); + StringRef line = lineAndRest.first; + buffer = lineAndRest.second; + // Ignore trailing # comments. + std::pair<StringRef, StringRef> symAndComment = line.split('#'); + if (symAndComment.first.empty()) + continue; + StringRef sym = symAndComment.first.trim(); + if (sym.empty()) + continue; + // Check for prefix. + StringRef prefix; + std::pair<StringRef, StringRef> prefixAndSym = sym.split(':'); + if (!prefixAndSym.second.empty()) { + sym = prefixAndSym.second; + prefix = prefixAndSym.first; + if (!prefix.endswith(".o") && !prefix.endswith(".o)")) { + // If arch name prefix does not match arch being linked, ignore symbol. + if (!ctx.archName().equals(prefix)) + continue; + prefix = ""; + } + } else + sym = prefixAndSym.first; + if (!sym.empty()) { + ctx.appendOrderedSymbol(sym, prefix); + //llvm::errs() << sym << ", prefix=" << prefix << "\n"; + } + } + return std::error_code(); +} + +// +// There are two variants of the -filelist option: +// +// -filelist <path> +// In this variant, the path is to a text file which contains one file path +// per line. There are no comments or trimming of whitespace. +// +// -fileList <path>,<dir> +// In this variant, the path is to a text file which contains a partial path +// per line. The <dir> prefix is prepended to each partial path. +// +static std::error_code loadFileList(StringRef fileListPath, + MachOLinkingContext &ctx, bool forceLoad, + raw_ostream &diagnostics) { + // If there is a comma, split off <dir>. + std::pair<StringRef, StringRef> opt = fileListPath.split(','); + StringRef filePath = opt.first; + StringRef dirName = opt.second; + ctx.addInputFileDependency(filePath); + // Map in file list file. + ErrorOr<std::unique_ptr<MemoryBuffer>> mb = + MemoryBuffer::getFileOrSTDIN(filePath); + if (std::error_code ec = mb.getError()) + return ec; + StringRef buffer = mb->get()->getBuffer(); + while (!buffer.empty()) { + // Split off each line in the file. + std::pair<StringRef, StringRef> lineAndRest = buffer.split('\n'); + StringRef line = lineAndRest.first; + StringRef path; + if (!dirName.empty()) { + // If there is a <dir> then prepend dir to each line. + SmallString<256> fullPath; + fullPath.assign(dirName); + llvm::sys::path::append(fullPath, Twine(line)); + path = ctx.copy(fullPath.str()); + } else { + // No <dir> use whole line as input file path. + path = ctx.copy(line); + } + if (!ctx.pathExists(path)) { + return make_dynamic_error_code(Twine("File not found '") + + path + + "'"); + } + if (ctx.testingFileUsage()) { + diagnostics << "Found filelist entry " << canonicalizePath(path) << '\n'; + } + addFile(path, ctx, forceLoad, false, diagnostics); + buffer = lineAndRest.second; + } + return std::error_code(); +} + +/// Parse number assuming it is base 16, but allow 0x prefix. +static bool parseNumberBase16(StringRef numStr, uint64_t &baseAddress) { + if (numStr.startswith_lower("0x")) + numStr = numStr.drop_front(2); + return numStr.getAsInteger(16, baseAddress); +} + +namespace lld { + +bool DarwinLdDriver::linkMachO(int argc, const char *argv[], + raw_ostream &diagnostics) { + MachOLinkingContext ctx; + if (!parse(argc, argv, ctx, diagnostics)) + return false; + if (ctx.doNothing()) + return true; + return link(ctx, diagnostics); +} + +bool DarwinLdDriver::parse(int argc, const char *argv[], + MachOLinkingContext &ctx, raw_ostream &diagnostics) { + // Parse command line options using DarwinLdOptions.td + std::unique_ptr<llvm::opt::InputArgList> parsedArgs; + DarwinLdOptTable table; + unsigned missingIndex; + unsigned missingCount; + bool globalWholeArchive = false; + parsedArgs.reset( + table.ParseArgs(&argv[1], &argv[argc], missingIndex, missingCount)); + if (missingCount) { + diagnostics << "error: missing arg value for '" + << parsedArgs->getArgString(missingIndex) << "' expected " + << missingCount << " argument(s).\n"; + return false; + } + + for (auto unknownArg : parsedArgs->filtered(OPT_UNKNOWN)) { + diagnostics << "warning: ignoring unknown argument: " + << unknownArg->getAsString(*parsedArgs) << "\n"; + } + + // Figure out output kind ( -dylib, -r, -bundle, -preload, or -static ) + llvm::MachO::HeaderFileType fileType = llvm::MachO::MH_EXECUTE; + if ( llvm::opt::Arg *kind = parsedArgs->getLastArg(OPT_dylib, OPT_relocatable, + OPT_bundle, OPT_static, OPT_preload)) { + switch (kind->getOption().getID()) { + case OPT_dylib: + fileType = llvm::MachO::MH_DYLIB; + break; + case OPT_relocatable: + fileType = llvm::MachO::MH_OBJECT; + break; + case OPT_bundle: + fileType = llvm::MachO::MH_BUNDLE; + break; + case OPT_static: + fileType = llvm::MachO::MH_EXECUTE; + break; + case OPT_preload: + fileType = llvm::MachO::MH_PRELOAD; + break; + } + } + + // Handle -arch xxx + MachOLinkingContext::Arch arch = MachOLinkingContext::arch_unknown; + if (llvm::opt::Arg *archStr = parsedArgs->getLastArg(OPT_arch)) { + arch = MachOLinkingContext::archFromName(archStr->getValue()); + if (arch == MachOLinkingContext::arch_unknown) { + diagnostics << "error: unknown arch named '" << archStr->getValue() + << "'\n"; + return false; + } + } + // If no -arch specified, scan input files to find first non-fat .o file. + if (arch == MachOLinkingContext::arch_unknown) { + for (auto &inFile: parsedArgs->filtered(OPT_INPUT)) { + // This is expensive because it opens and maps the file. But that is + // ok because no -arch is rare. + if (MachOLinkingContext::isThinObjectFile(inFile->getValue(), arch)) + break; + } + if (arch == MachOLinkingContext::arch_unknown + && !parsedArgs->getLastArg(OPT_test_file_usage)) { + // If no -arch and no options at all, print usage message. + if (parsedArgs->size() == 0) + table.PrintHelp(llvm::outs(), argv[0], "LLVM Linker", false); + else + diagnostics << "error: -arch not specified and could not be inferred\n"; + return false; + } + } + + // Handle -macosx_version_min or -ios_version_min + MachOLinkingContext::OS os = MachOLinkingContext::OS::macOSX; + uint32_t minOSVersion = 0; + if (llvm::opt::Arg *minOS = + parsedArgs->getLastArg(OPT_macosx_version_min, OPT_ios_version_min, + OPT_ios_simulator_version_min)) { + switch (minOS->getOption().getID()) { + case OPT_macosx_version_min: + os = MachOLinkingContext::OS::macOSX; + if (MachOLinkingContext::parsePackedVersion(minOS->getValue(), + minOSVersion)) { + diagnostics << "error: malformed macosx_version_min value\n"; + return false; + } + break; + case OPT_ios_version_min: + os = MachOLinkingContext::OS::iOS; + if (MachOLinkingContext::parsePackedVersion(minOS->getValue(), + minOSVersion)) { + diagnostics << "error: malformed ios_version_min value\n"; + return false; + } + break; + case OPT_ios_simulator_version_min: + os = MachOLinkingContext::OS::iOS_simulator; + if (MachOLinkingContext::parsePackedVersion(minOS->getValue(), + minOSVersion)) { + diagnostics << "error: malformed ios_simulator_version_min value\n"; + return false; + } + break; + } + } else { + // No min-os version on command line, check environment variables + } + + // Now that there's enough information parsed in, let the linking context + // set up default values. + ctx.configure(fileType, arch, os, minOSVersion); + + // Handle -e xxx + if (llvm::opt::Arg *entry = parsedArgs->getLastArg(OPT_entry)) + ctx.setEntrySymbolName(entry->getValue()); + + // Handle -o xxx + if (llvm::opt::Arg *outpath = parsedArgs->getLastArg(OPT_output)) + ctx.setOutputPath(outpath->getValue()); + else + ctx.setOutputPath("a.out"); + + // Handle -image_base XXX and -seg1addr XXXX + if (llvm::opt::Arg *imageBase = parsedArgs->getLastArg(OPT_image_base)) { + uint64_t baseAddress; + if (parseNumberBase16(imageBase->getValue(), baseAddress)) { + diagnostics << "error: image_base expects a hex number\n"; + return false; + } else if (baseAddress < ctx.pageZeroSize()) { + diagnostics << "error: image_base overlaps with __PAGEZERO\n"; + return false; + } else if (baseAddress % ctx.pageSize()) { + diagnostics << "error: image_base must be a multiple of page size (" + << llvm::format("0x%" PRIx64, ctx.pageSize()) << ")\n"; + return false; + } + + ctx.setBaseAddress(baseAddress); + } + + // Handle -dead_strip + if (parsedArgs->getLastArg(OPT_dead_strip)) + ctx.setDeadStripping(true); + + // Handle -all_load + if (parsedArgs->getLastArg(OPT_all_load)) + globalWholeArchive = true; + + // Handle -install_name + if (llvm::opt::Arg *installName = parsedArgs->getLastArg(OPT_install_name)) + ctx.setInstallName(installName->getValue()); + else + ctx.setInstallName(ctx.outputPath()); + + // Handle -mark_dead_strippable_dylib + if (parsedArgs->getLastArg(OPT_mark_dead_strippable_dylib)) + ctx.setDeadStrippableDylib(true); + + // Handle -compatibility_version and -current_version + if (llvm::opt::Arg *vers = + parsedArgs->getLastArg(OPT_compatibility_version)) { + if (ctx.outputMachOType() != llvm::MachO::MH_DYLIB) { + diagnostics + << "error: -compatibility_version can only be used with -dylib\n"; + return false; + } + uint32_t parsedVers; + if (MachOLinkingContext::parsePackedVersion(vers->getValue(), parsedVers)) { + diagnostics << "error: -compatibility_version value is malformed\n"; + return false; + } + ctx.setCompatibilityVersion(parsedVers); + } + + if (llvm::opt::Arg *vers = parsedArgs->getLastArg(OPT_current_version)) { + if (ctx.outputMachOType() != llvm::MachO::MH_DYLIB) { + diagnostics << "-current_version can only be used with -dylib\n"; + return false; + } + uint32_t parsedVers; + if (MachOLinkingContext::parsePackedVersion(vers->getValue(), parsedVers)) { + diagnostics << "error: -current_version value is malformed\n"; + return false; + } + ctx.setCurrentVersion(parsedVers); + } + + // Handle -bundle_loader + if (llvm::opt::Arg *loader = parsedArgs->getLastArg(OPT_bundle_loader)) + ctx.setBundleLoader(loader->getValue()); + + // Handle -sectalign segname sectname align + for (auto &alignArg : parsedArgs->filtered(OPT_sectalign)) { + const char* segName = alignArg->getValue(0); + const char* sectName = alignArg->getValue(1); + const char* alignStr = alignArg->getValue(2); + if ((alignStr[0] == '0') && (alignStr[1] == 'x')) + alignStr += 2; + unsigned long long alignValue; + if (llvm::getAsUnsignedInteger(alignStr, 16, alignValue)) { + diagnostics << "error: -sectalign alignment value '" + << alignStr << "' not a valid number\n"; + return false; + } + uint8_t align2 = llvm::countTrailingZeros(alignValue); + if ( (unsigned long)(1 << align2) != alignValue ) { + diagnostics << "warning: alignment for '-sectalign " + << segName << " " << sectName + << llvm::format(" 0x%llX", alignValue) + << "' is not a power of two, using " + << llvm::format("0x%08X", (1 << align2)) << "\n"; + } + ctx.addSectionAlignment(segName, sectName, align2); + } + + // Handle -mllvm + for (auto &llvmArg : parsedArgs->filtered(OPT_mllvm)) { + ctx.appendLLVMOption(llvmArg->getValue()); + } + + // Handle -print_atoms + if (parsedArgs->getLastArg(OPT_print_atoms)) + ctx.setPrintAtoms(); + + // Handle -t (trace) option. + if (parsedArgs->getLastArg(OPT_t)) + ctx.setLogInputFiles(true); + + // Handle -demangle option. + if (parsedArgs->getLastArg(OPT_demangle)) + ctx.setDemangleSymbols(true); + + // Handle -keep_private_externs + if (parsedArgs->getLastArg(OPT_keep_private_externs)) { + ctx.setKeepPrivateExterns(true); + if (ctx.outputMachOType() != llvm::MachO::MH_OBJECT) + diagnostics << "warning: -keep_private_externs only used in -r mode\n"; + } + + // Handle -dependency_info <path> used by Xcode. + if (llvm::opt::Arg *depInfo = parsedArgs->getLastArg(OPT_dependency_info)) { + if (std::error_code ec = ctx.createDependencyFile(depInfo->getValue())) { + diagnostics << "warning: " << ec.message() + << ", processing '-dependency_info " + << depInfo->getValue() + << "'\n"; + } + } + + // In -test_file_usage mode, we'll be given an explicit list of paths that + // exist. We'll also be expected to print out information about how we located + // libraries and so on that the user specified, but not to actually do any + // linking. + if (parsedArgs->getLastArg(OPT_test_file_usage)) { + ctx.setTestingFileUsage(); + + // With paths existing by fiat, linking is not going to end well. + ctx.setDoNothing(true); + + // Only bother looking for an existence override if we're going to use it. + for (auto existingPath : parsedArgs->filtered(OPT_path_exists)) { + ctx.addExistingPathForDebug(existingPath->getValue()); + } + } + + // Register possible input file parsers. + if (!ctx.doNothing()) { + ctx.registry().addSupportMachOObjects(ctx); + ctx.registry().addSupportArchives(ctx.logInputFiles()); + ctx.registry().addSupportNativeObjects(); + ctx.registry().addSupportYamlFiles(); + } + + // Now construct the set of library search directories, following ld64's + // baroque set of accumulated hacks. Mostly, the algorithm constructs + // { syslibroots } x { libpaths } + // + // Unfortunately, there are numerous exceptions: + // 1. Only absolute paths get modified by syslibroot options. + // 2. If there is just 1 -syslibroot, system paths not found in it are + // skipped. + // 3. If the last -syslibroot is "/", all of them are ignored entirely. + // 4. If { syslibroots } x path == {}, the original path is kept. + std::vector<StringRef> sysLibRoots; + for (auto syslibRoot : parsedArgs->filtered(OPT_syslibroot)) { + sysLibRoots.push_back(syslibRoot->getValue()); + } + if (!sysLibRoots.empty()) { + // Ignore all if last -syslibroot is "/". + if (sysLibRoots.back() != "/") + ctx.setSysLibRoots(sysLibRoots); + } + + // Paths specified with -L come first, and are not considered system paths for + // the case where there is precisely 1 -syslibroot. + for (auto libPath : parsedArgs->filtered(OPT_L)) { + ctx.addModifiedSearchDir(libPath->getValue()); + } + + // Process -F directories (where to look for frameworks). + for (auto fwPath : parsedArgs->filtered(OPT_F)) { + ctx.addFrameworkSearchDir(fwPath->getValue()); + } + + // -Z suppresses the standard search paths. + if (!parsedArgs->hasArg(OPT_Z)) { + ctx.addModifiedSearchDir("/usr/lib", true); + ctx.addModifiedSearchDir("/usr/local/lib", true); + ctx.addFrameworkSearchDir("/Library/Frameworks", true); + ctx.addFrameworkSearchDir("/System/Library/Frameworks", true); + } + + // Now that we've constructed the final set of search paths, print out those + // search paths in verbose mode. + if (parsedArgs->getLastArg(OPT_v)) { + diagnostics << "Library search paths:\n"; + for (auto path : ctx.searchDirs()) { + diagnostics << " " << path << '\n'; + } + diagnostics << "Framework search paths:\n"; + for (auto path : ctx.frameworkDirs()) { + diagnostics << " " << path << '\n'; + } + } + + // Handle -exported_symbols_list <file> + for (auto expFile : parsedArgs->filtered(OPT_exported_symbols_list)) { + if (ctx.exportMode() == MachOLinkingContext::ExportMode::blackList) { + diagnostics << "error: -exported_symbols_list cannot be combined " + << "with -unexported_symbol[s_list]\n"; + return false; + } + ctx.setExportMode(MachOLinkingContext::ExportMode::whiteList); + if (std::error_code ec = parseExportsList(expFile->getValue(), ctx, + diagnostics)) { + diagnostics << "error: " << ec.message() + << ", processing '-exported_symbols_list " + << expFile->getValue() + << "'\n"; + return false; + } + } + + // Handle -exported_symbol <symbol> + for (auto symbol : parsedArgs->filtered(OPT_exported_symbol)) { + if (ctx.exportMode() == MachOLinkingContext::ExportMode::blackList) { + diagnostics << "error: -exported_symbol cannot be combined " + << "with -unexported_symbol[s_list]\n"; + return false; + } + ctx.setExportMode(MachOLinkingContext::ExportMode::whiteList); + ctx.addExportSymbol(symbol->getValue()); + } + + // Handle -unexported_symbols_list <file> + for (auto expFile : parsedArgs->filtered(OPT_unexported_symbols_list)) { + if (ctx.exportMode() == MachOLinkingContext::ExportMode::whiteList) { + diagnostics << "error: -unexported_symbols_list cannot be combined " + << "with -exported_symbol[s_list]\n"; + return false; + } + ctx.setExportMode(MachOLinkingContext::ExportMode::blackList); + if (std::error_code ec = parseExportsList(expFile->getValue(), ctx, + diagnostics)) { + diagnostics << "error: " << ec.message() + << ", processing '-unexported_symbols_list " + << expFile->getValue() + << "'\n"; + return false; + } + } + + // Handle -unexported_symbol <symbol> + for (auto symbol : parsedArgs->filtered(OPT_unexported_symbol)) { + if (ctx.exportMode() == MachOLinkingContext::ExportMode::whiteList) { + diagnostics << "error: -unexported_symbol cannot be combined " + << "with -exported_symbol[s_list]\n"; + return false; + } + ctx.setExportMode(MachOLinkingContext::ExportMode::blackList); + ctx.addExportSymbol(symbol->getValue()); + } + + // Handle obosolete -multi_module and -single_module + if (llvm::opt::Arg *mod = parsedArgs->getLastArg(OPT_multi_module, + OPT_single_module)) { + if (mod->getOption().getID() == OPT_multi_module) { + diagnostics << "warning: -multi_module is obsolete and being ignored\n"; + } + else { + if (ctx.outputMachOType() != llvm::MachO::MH_DYLIB) { + diagnostics << "warning: -single_module being ignored. " + "It is only for use when producing a dylib\n"; + } + } + } + + // Handle -pie or -no_pie + if (llvm::opt::Arg *pie = parsedArgs->getLastArg(OPT_pie, OPT_no_pie)) { + switch (ctx.outputMachOType()) { + case llvm::MachO::MH_EXECUTE: + switch (ctx.os()) { + case MachOLinkingContext::OS::macOSX: + if ((minOSVersion < 0x000A0500) && + (pie->getOption().getID() == OPT_pie)) { + diagnostics << "-pie can only be used when targeting " + "Mac OS X 10.5 or later\n"; + return false; + } + break; + case MachOLinkingContext::OS::iOS: + if ((minOSVersion < 0x00040200) && + (pie->getOption().getID() == OPT_pie)) { + diagnostics << "-pie can only be used when targeting " + "iOS 4.2 or later\n"; + return false; + } + break; + case MachOLinkingContext::OS::iOS_simulator: + if (pie->getOption().getID() == OPT_no_pie) + diagnostics << "iOS simulator programs must be built PIE\n"; + return false; + break; + case MachOLinkingContext::OS::unknown: + break; + } + ctx.setPIE(pie->getOption().getID() == OPT_pie); + break; + case llvm::MachO::MH_PRELOAD: + break; + case llvm::MachO::MH_DYLIB: + case llvm::MachO::MH_BUNDLE: + diagnostics << "warning: " << pie->getSpelling() << " being ignored. " + << "It is only used when linking main executables\n"; + break; + default: + diagnostics << pie->getSpelling() + << " can only used when linking main executables\n"; + return false; + break; + } + } + + // Handle debug info handling options: -S + if (parsedArgs->hasArg(OPT_S)) + ctx.setDebugInfoMode(MachOLinkingContext::DebugInfoMode::noDebugMap); + + // Handle -order_file <file> + for (auto orderFile : parsedArgs->filtered(OPT_order_file)) { + if (std::error_code ec = parseOrderFile(orderFile->getValue(), ctx, + diagnostics)) { + diagnostics << "error: " << ec.message() + << ", processing '-order_file " + << orderFile->getValue() + << "'\n"; + return false; + } + } + + // Handle -rpath <path> + if (parsedArgs->hasArg(OPT_rpath)) { + switch (ctx.outputMachOType()) { + case llvm::MachO::MH_EXECUTE: + case llvm::MachO::MH_DYLIB: + case llvm::MachO::MH_BUNDLE: + if (!ctx.minOS("10.5", "2.0")) { + if (ctx.os() == MachOLinkingContext::OS::macOSX) { + diagnostics << "error: -rpath can only be used when targeting " + "OS X 10.5 or later\n"; + } else { + diagnostics << "error: -rpath can only be used when targeting " + "iOS 2.0 or later\n"; + } + return false; + } + break; + default: + diagnostics << "error: -rpath can only be used when creating " + "a dynamic final linked image\n"; + return false; + } + + for (auto rPath : parsedArgs->filtered(OPT_rpath)) { + ctx.addRpath(rPath->getValue()); + } + } + + // Handle input files + for (auto &arg : *parsedArgs) { + bool upward; + ErrorOr<StringRef> resolvedPath = StringRef(); + switch (arg->getOption().getID()) { + default: + continue; + case OPT_INPUT: + addFile(arg->getValue(), ctx, globalWholeArchive, false, diagnostics); + break; + case OPT_upward_library: + addFile(arg->getValue(), ctx, false, true, diagnostics); + break; + case OPT_force_load: + addFile(arg->getValue(), ctx, true, false, diagnostics); + break; + case OPT_l: + case OPT_upward_l: + upward = (arg->getOption().getID() == OPT_upward_l); + resolvedPath = ctx.searchLibrary(arg->getValue()); + if (!resolvedPath) { + diagnostics << "Unable to find library for " << arg->getSpelling() + << arg->getValue() << "\n"; + return false; + } else if (ctx.testingFileUsage()) { + diagnostics << "Found " << (upward ? "upward " : " ") << "library " + << canonicalizePath(resolvedPath.get()) << '\n'; + } + addFile(resolvedPath.get(), ctx, globalWholeArchive, upward, diagnostics); + break; + case OPT_framework: + case OPT_upward_framework: + upward = (arg->getOption().getID() == OPT_upward_framework); + resolvedPath = ctx.findPathForFramework(arg->getValue()); + if (!resolvedPath) { + diagnostics << "Unable to find framework for " + << arg->getSpelling() << " " << arg->getValue() << "\n"; + return false; + } else if (ctx.testingFileUsage()) { + diagnostics << "Found " << (upward ? "upward " : " ") << "framework " + << canonicalizePath(resolvedPath.get()) << '\n'; + } + addFile(resolvedPath.get(), ctx, globalWholeArchive, upward, diagnostics); + break; + case OPT_filelist: + if (std::error_code ec = loadFileList(arg->getValue(), + ctx, globalWholeArchive, + diagnostics)) { + diagnostics << "error: " << ec.message() + << ", processing '-filelist " << arg->getValue() + << "'\n"; + return false; + } + break; + } + } + + if (ctx.getNodes().empty()) { + diagnostics << "No input files\n"; + return false; + } + + // Validate the combination of options used. + return ctx.validate(diagnostics); +} + + +} // namespace lld diff --git a/lib/Driver/DarwinLdOptions.td b/lib/Driver/DarwinLdOptions.td new file mode 100644 index 0000000000000..81dcc0a1d9251 --- /dev/null +++ b/lib/Driver/DarwinLdOptions.td @@ -0,0 +1,187 @@ +include "llvm/Option/OptParser.td" + + +// output kinds +def grp_kind : OptionGroup<"outs">, HelpText<"OUTPUT KIND">; +def relocatable : Flag<["-"], "r">, + HelpText<"Create relocatable object file">, Group<grp_kind>; +def static : Flag<["-"], "static">, + HelpText<"Create static executable">, Group<grp_kind>; +def dynamic : Flag<["-"], "dynamic">, + HelpText<"Create dynamic executable (default)">,Group<grp_kind>; +def dylib : Flag<["-"], "dylib">, + HelpText<"Create dynamic library">, Group<grp_kind>; +def bundle : Flag<["-"], "bundle">, + HelpText<"Create dynamic bundle">, Group<grp_kind>; +def execute : Flag<["-"], "execute">, + HelpText<"Create main executable (default)">, Group<grp_kind>; +def preload : Flag<["-"], "preload">, + HelpText<"Create binary for use with embedded systems">, Group<grp_kind>; + +// optimizations +def grp_opts : OptionGroup<"opts">, HelpText<"OPTIMIZATIONS">; +def dead_strip : Flag<["-"], "dead_strip">, + HelpText<"Remove unreference code and data">, Group<grp_opts>; +def macosx_version_min : Separate<["-"], "macosx_version_min">, + MetaVarName<"<version>">, + HelpText<"Minimum Mac OS X version">, Group<grp_opts>; +def ios_version_min : Separate<["-"], "ios_version_min">, + MetaVarName<"<version>">, + HelpText<"Minimum iOS version">, Group<grp_opts>; +def iphoneos_version_min : Separate<["-"], "iphoneos_version_min">, + Alias<ios_version_min>; +def ios_simulator_version_min : Separate<["-"], "ios_simulator_version_min">, + MetaVarName<"<version>">, + HelpText<"Minimum iOS simulator version">, Group<grp_opts>; +def mllvm : Separate<["-"], "mllvm">, + MetaVarName<"<option>">, + HelpText<"Options to pass to LLVM during LTO">, Group<grp_opts>; +def exported_symbols_list : Separate<["-"], "exported_symbols_list">, + MetaVarName<"<file-path>">, + HelpText<"Restricts which symbols will be exported">, Group<grp_opts>; +def exported_symbol : Separate<["-"], "exported_symbol">, + MetaVarName<"<symbol>">, + HelpText<"Restricts which symbols will be exported">, Group<grp_opts>; +def unexported_symbols_list : Separate<["-"], "unexported_symbols_list">, + MetaVarName<"<file-path>">, + HelpText<"Lists symbols that should not be exported">, Group<grp_opts>; +def unexported_symbol : Separate<["-"], "unexported_symbol">, + MetaVarName<"<symbol>">, + HelpText<"A symbol which should not be exported">, Group<grp_opts>; +def keep_private_externs : Flag<["-"], "keep_private_externs">, + HelpText<"Private extern (hidden) symbols should not be transformed " + "into local symbols">, Group<grp_opts>; +def order_file : Separate<["-"], "order_file">, + MetaVarName<"<file-path>">, + HelpText<"re-order and move specified symbols to start of their section">, + Group<grp_opts>; + +// main executable options +def grp_main : OptionGroup<"opts">, HelpText<"MAIN EXECUTABLE OPTIONS">; +def entry : Separate<["-"], "e">, + MetaVarName<"<entry-name>">, + HelpText<"entry symbol name">,Group<grp_main>; +def pie : Flag<["-"], "pie">, + HelpText<"Create Position Independent Executable (for ASLR)">, + Group<grp_main>; +def no_pie : Flag<["-"], "no_pie">, + HelpText<"Do not create Position Independent Executable">, + Group<grp_main>; + +// dylib executable options +def grp_dylib : OptionGroup<"opts">, HelpText<"DYLIB EXECUTABLE OPTIONS">; +def install_name : Separate<["-"], "install_name">, + MetaVarName<"<path>">, + HelpText<"The dylib's install name">, Group<grp_dylib>; +def mark_dead_strippable_dylib : Flag<["-"], "mark_dead_strippable_dylib">, + HelpText<"Marks the dylib as having no side effects during initialization">, + Group<grp_dylib>; +def compatibility_version : Separate<["-"], "compatibility_version">, + MetaVarName<"<version>">, + HelpText<"The dylib's compatibility version">, Group<grp_dylib>; +def current_version : Separate<["-"], "current_version">, + MetaVarName<"<version>">, + HelpText<"The dylib's current version">, Group<grp_dylib>; + +// dylib executable options - compatibility aliases +def dylib_install_name : Separate<["-"], "dylib_install_name">, + Alias<install_name>; +def dylib_compatibility_version : Separate<["-"], "dylib_compatibility_version">, + MetaVarName<"<version>">, Alias<compatibility_version>; +def dylib_current_version : Separate<["-"], "dylib_current_version">, + MetaVarName<"<version>">, Alias<current_version>; + +// bundle executable options +def grp_bundle : OptionGroup<"opts">, HelpText<"BUNDLE EXECUTABLE OPTIONS">; +def bundle_loader : Separate<["-"], "bundle_loader">, + MetaVarName<"<path>">, + HelpText<"The executable that will be loading this Mach-O bundle">, + Group<grp_bundle>; + +// library options +def grp_libs : OptionGroup<"libs">, HelpText<"LIBRARY OPTIONS">; +def L : JoinedOrSeparate<["-"], "L">, + MetaVarName<"<dir>">, + HelpText<"Add directory to library search path">, Group<grp_libs>; +def F : JoinedOrSeparate<["-"], "F">, + MetaVarName<"<dir>">, + HelpText<"Add directory to framework search path">, Group<grp_libs>; +def Z : Flag<["-"], "Z">, + HelpText<"Do not search standard directories for libraries or frameworks">; +def all_load : Flag<["-"], "all_load">, + HelpText<"Forces all members of all static libraries to be loaded">, + Group<grp_libs>; +def force_load : Separate<["-"], "force_load">, + MetaVarName<"<library-path>">, + HelpText<"Forces all members of specified static libraries to be loaded">, + Group<grp_libs>; +def syslibroot : Separate<["-"], "syslibroot">, MetaVarName<"<dir>">, + HelpText<"Add path to SDK to all absolute library search paths">, + Group<grp_libs>; + +// Input options +def l : Joined<["-"], "l">, + MetaVarName<"<libname>">, + HelpText<"Base name of library searched for in -L directories">; +def upward_l : Joined<["-"], "upward-l">, + MetaVarName<"<libname>">, + HelpText<"Base name of upward library searched for in -L directories">; +def framework : Separate<["-"], "framework">, + MetaVarName<"<name>">, + HelpText<"Base name of framework searched for in -F directories">; +def upward_framework : Separate<["-"], "upward_framework">, + MetaVarName<"<name>">, + HelpText<"Base name of upward framework searched for in -F directories">; +def upward_library : Separate<["-"], "upward_library">, + MetaVarName<"<path>">, + HelpText<"path to upward dylib to link with">; +def filelist : Separate<["-"], "filelist">, + MetaVarName<"<path>">, + HelpText<"file containing paths to input files">; + + +// test case options +def print_atoms : Flag<["-"], "print_atoms">, + HelpText<"Emit output as yaml atoms">; +def test_file_usage : Flag<["-"], "test_file_usage">, + HelpText<"Only files specified by -file_exists are considered to exist. " + "Print which files would be used">; +def path_exists : Separate<["-"], "path_exists">, + MetaVarName<"<path>">, + HelpText<"Used with -test_file_usage to declare a path">; + + +// general options +def output : Separate<["-"], "o">, + MetaVarName<"<path>">, + HelpText<"Output file path">; +def arch : Separate<["-"], "arch">, + MetaVarName<"<arch-name>">, + HelpText<"Architecture to link">; +def sectalign : MultiArg<["-"], "sectalign", 3>, + MetaVarName<"<segname> <sectname> <alignment>">, + HelpText<"alignment for segment/section">; +def image_base : Separate<["-"], "image_base">; +def seg1addr : Separate<["-"], "seg1addr">, Alias<image_base>; +def demangle : Flag<["-"], "demangle">, + HelpText<"Demangles symbol names in errors and warnings">; +def dependency_info : Separate<["-"], "dependency_info">, + MetaVarName<"<file>">, + HelpText<"Write binary list of files used during link">; +def S : Flag<["-"], "S">, + HelpText<"Remove debug information (STABS or DWARF) from the output file">; +def rpath : Separate<["-"], "rpath">, + MetaVarName<"<path>">, + HelpText<"Add path to the runpath search path list for image being created">; + +def t : Flag<["-"], "t">, + HelpText<"Print the names of the input files as ld processes them">; +def v : Flag<["-"], "v">, + HelpText<"Print linker information">; + +// Obsolete options +def grp_obsolete : OptionGroup<"obsolete">, HelpText<"OBSOLETE OPTIONS">; +def single_module : Flag<["-"], "single_module">, + HelpText<"Default for dylibs">, Group<grp_obsolete>; +def multi_module : Flag<["-"], "multi_module">, + HelpText<"Unsupported way to build dylibs">, Group<grp_obsolete>; diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp new file mode 100644 index 0000000000000..d32bfa6e47beb --- /dev/null +++ b/lib/Driver/Driver.cpp @@ -0,0 +1,130 @@ +//===- lib/Driver/Driver.cpp - Linker Driver Emulator ---------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "lld/Core/ArchiveLibraryFile.h" +#include "lld/Core/File.h" +#include "lld/Core/Instrumentation.h" +#include "lld/Core/LLVM.h" +#include "lld/Core/Parallel.h" +#include "lld/Core/PassManager.h" +#include "lld/Core/Reader.h" +#include "lld/Core/Resolver.h" +#include "lld/Core/Writer.h" +#include "lld/Driver/Driver.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Option/Arg.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/raw_ostream.h" +#include <mutex> + +namespace lld { + +FileVector makeErrorFile(StringRef path, std::error_code ec) { + std::vector<std::unique_ptr<File>> result; + result.push_back(llvm::make_unique<ErrorFile>(path, ec)); + return result; +} + +FileVector parseMemberFiles(FileVector &files) { + std::vector<std::unique_ptr<File>> members; + for (std::unique_ptr<File> &file : files) { + if (auto *archive = dyn_cast<ArchiveLibraryFile>(file.get())) { + if (std::error_code ec = archive->parseAllMembers(members)) + return makeErrorFile(file->path(), ec); + } else { + members.push_back(std::move(file)); + } + } + return members; +} + +FileVector loadFile(LinkingContext &ctx, StringRef path, bool wholeArchive) { + ErrorOr<std::unique_ptr<MemoryBuffer>> mb + = MemoryBuffer::getFileOrSTDIN(path); + if (std::error_code ec = mb.getError()) + return makeErrorFile(path, ec); + std::vector<std::unique_ptr<File>> files; + if (std::error_code ec = ctx.registry().loadFile(std::move(mb.get()), files)) + return makeErrorFile(path, ec); + if (wholeArchive) + return parseMemberFiles(files); + return files; +} + +/// This is where the link is actually performed. +bool Driver::link(LinkingContext &context, raw_ostream &diagnostics) { + // Honor -mllvm + if (!context.llvmOptions().empty()) { + unsigned numArgs = context.llvmOptions().size(); + const char **args = new const char *[numArgs + 2]; + args[0] = "lld (LLVM option parsing)"; + for (unsigned i = 0; i != numArgs; ++i) + args[i + 1] = context.llvmOptions()[i]; + args[numArgs + 1] = 0; + llvm::cl::ParseCommandLineOptions(numArgs + 1, args); + } + if (context.getNodes().empty()) + return false; + + for (std::unique_ptr<Node> &ie : context.getNodes()) + if (FileNode *node = dyn_cast<FileNode>(ie.get())) + context.getTaskGroup().spawn([node] { node->getFile()->parse(); }); + + std::vector<std::unique_ptr<File>> internalFiles; + context.createInternalFiles(internalFiles); + for (auto i = internalFiles.rbegin(), e = internalFiles.rend(); i != e; ++i) { + auto &members = context.getNodes(); + members.insert(members.begin(), llvm::make_unique<FileNode>(std::move(*i))); + } + + // Give target a chance to add files. + std::vector<std::unique_ptr<File>> implicitFiles; + context.createImplicitFiles(implicitFiles); + for (auto i = implicitFiles.rbegin(), e = implicitFiles.rend(); i != e; ++i) { + auto &members = context.getNodes(); + members.insert(members.begin(), llvm::make_unique<FileNode>(std::move(*i))); + } + + // Give target a chance to postprocess input files. + // Mach-O uses this chance to move all object files before library files. + // ELF adds specific undefined symbols resolver. + context.finalizeInputFiles(); + + // Do core linking. + ScopedTask resolveTask(getDefaultDomain(), "Resolve"); + Resolver resolver(context); + if (!resolver.resolve()) + return false; + std::unique_ptr<MutableFile> merged = resolver.resultFile(); + resolveTask.end(); + + // Run passes on linked atoms. + ScopedTask passTask(getDefaultDomain(), "Passes"); + PassManager pm; + context.addPasses(pm); + pm.runOnFile(merged); + passTask.end(); + + // Give linked atoms to Writer to generate output file. + ScopedTask writeTask(getDefaultDomain(), "Write"); + if (std::error_code ec = context.writeFile(*merged)) { + diagnostics << "Failed to write file '" << context.outputPath() + << "': " << ec.message() << "\n"; + return false; + } + + return true; +} + +} // namespace diff --git a/lib/Driver/GnuLdDriver.cpp b/lib/Driver/GnuLdDriver.cpp new file mode 100644 index 0000000000000..b9af04d4b6152 --- /dev/null +++ b/lib/Driver/GnuLdDriver.cpp @@ -0,0 +1,760 @@ +//===- lib/Driver/GnuLdDriver.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 GNU's ld. +/// +//===----------------------------------------------------------------------===// + +#include "lld/Driver/Driver.h" +#include "lld/ReaderWriter/ELFLinkingContext.h" +#include "lld/ReaderWriter/ELFTargets.h" +#include "lld/ReaderWriter/LinkerScript.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/Option.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/ManagedStatic.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Signals.h" +#include "llvm/Support/Timer.h" +#include "llvm/Support/raw_ostream.h" +#include <cstring> +#include <tuple> + +using namespace lld; + +using llvm::BumpPtrAllocator; + +namespace { + +// Create enum with OPT_xxx values for each option in GnuLdOptions.td +enum { + OPT_INVALID = 0, +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELP, META) \ + OPT_##ID, +#include "GnuLdOptions.inc" +#undef OPTION +}; + +// Create prefix string literals used in GnuLdOptions.td +#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; +#include "GnuLdOptions.inc" +#undef PREFIX + +// Create table mapping all options defined in GnuLdOptions.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 "GnuLdOptions.inc" +#undef OPTION +}; + + +// Create OptTable class for parsing actual command line arguments +class GnuLdOptTable : public llvm::opt::OptTable { +public: + GnuLdOptTable() : OptTable(infoTable, llvm::array_lengthof(infoTable)){} +}; + +class DriverStringSaver : public llvm::cl::StringSaver { +public: + DriverStringSaver(BumpPtrAllocator &alloc) : _alloc(alloc) {} + + const char *SaveString(const char *s) override { + char *p = _alloc.Allocate<char>(strlen(s) + 1); + strcpy(p, s); + return p; + } + +private: + BumpPtrAllocator &_alloc; +}; + +} // anonymous namespace + +// If a command line option starts with "@", the driver reads its suffix as a +// file, parse its contents as a list of command line options, and insert them +// at the original @file position. If file cannot be read, @file is not expanded +// and left unmodified. @file can appear in a response file, so it's a recursive +// process. +static std::tuple<int, const char **> +maybeExpandResponseFiles(int argc, const char **argv, BumpPtrAllocator &alloc) { + // Expand response files. + SmallVector<const char *, 256> smallvec; + for (int i = 0; i < argc; ++i) + smallvec.push_back(argv[i]); + DriverStringSaver saver(alloc); + llvm::cl::ExpandResponseFiles(saver, llvm::cl::TokenizeGNUCommandLine, smallvec); + + // Pack the results to a C-array and return it. + argc = smallvec.size(); + const char **copy = alloc.Allocate<const char *>(argc + 1); + std::copy(smallvec.begin(), smallvec.end(), copy); + copy[argc] = nullptr; + return std::make_tuple(argc, copy); +} + +static std::error_code +getFileMagic(StringRef path, llvm::sys::fs::file_magic &magic) { + std::error_code ec = llvm::sys::fs::identify_magic(path, magic); + if (ec) + return ec; + switch (magic) { + case llvm::sys::fs::file_magic::archive: + case llvm::sys::fs::file_magic::elf_relocatable: + case llvm::sys::fs::file_magic::elf_shared_object: + case llvm::sys::fs::file_magic::unknown: + return std::error_code(); + default: + return make_dynamic_error_code(StringRef("unknown type of object file")); + } +} + +// Parses an argument of --defsym=<sym>=<number> +static bool parseDefsymAsAbsolute(StringRef opt, StringRef &sym, + uint64_t &addr) { + size_t equalPos = opt.find('='); + if (equalPos == 0 || equalPos == StringRef::npos) + return false; + sym = opt.substr(0, equalPos); + if (opt.substr(equalPos + 1).getAsInteger(0, addr)) + return false; + return true; +} + +// Parses an argument of --defsym=<sym>=<sym> +static bool parseDefsymAsAlias(StringRef opt, StringRef &sym, + StringRef &target) { + size_t equalPos = opt.find('='); + if (equalPos == 0 || equalPos == StringRef::npos) + return false; + sym = opt.substr(0, equalPos); + target = opt.substr(equalPos + 1); + return !target.empty(); +} + +// Parses -z max-page-size=<value> +static bool parseMaxPageSize(StringRef opt, uint64_t &val) { + size_t equalPos = opt.find('='); + if (equalPos == 0 || equalPos == StringRef::npos) + return false; + StringRef value = opt.substr(equalPos + 1); + val = 0; + if (value.getAsInteger(0, val) || !val) + return false; + return true; +} + +bool GnuLdDriver::linkELF(int argc, const char *argv[], raw_ostream &diag) { + BumpPtrAllocator alloc; + std::tie(argc, argv) = maybeExpandResponseFiles(argc, argv, alloc); + std::unique_ptr<ELFLinkingContext> options; + if (!parse(argc, argv, options, diag)) + return false; + if (!options) + return true; + bool linked = link(*options, diag); + + // Handle --stats. + if (options->collectStats()) { + llvm::TimeRecord t = llvm::TimeRecord::getCurrentTime(true); + diag << "total time in link " << t.getProcessTime() << "\n"; + diag << "data size " << t.getMemUsed() << "\n"; + } + return linked; +} + +static llvm::Optional<llvm::Triple::ArchType> +getArchType(const llvm::Triple &triple, StringRef value) { + switch (triple.getArch()) { + case llvm::Triple::x86: + case llvm::Triple::x86_64: + if (value == "elf_i386") + return llvm::Triple::x86; + if (value == "elf_x86_64") + return llvm::Triple::x86_64; + return llvm::None; + case llvm::Triple::mipsel: + case llvm::Triple::mips64el: + if (value == "elf32ltsmip") + return llvm::Triple::mipsel; + if (value == "elf64ltsmip") + return llvm::Triple::mips64el; + return llvm::None; + case llvm::Triple::aarch64: + if (value == "aarch64linux") + return llvm::Triple::aarch64; + return llvm::None; + case llvm::Triple::arm: + if (value == "armelf_linux_eabi") + return llvm::Triple::arm; + return llvm::None; + default: + return llvm::None; + } +} + +static bool isLinkerScript(StringRef path, raw_ostream &diag) { + llvm::sys::fs::file_magic magic = llvm::sys::fs::file_magic::unknown; + std::error_code ec = getFileMagic(path, magic); + if (ec) { + diag << "unknown input file format for file " << path << "\n"; + return false; + } + return magic == llvm::sys::fs::file_magic::unknown; +} + +static ErrorOr<StringRef> +findFile(ELFLinkingContext &ctx, StringRef path, bool dashL) { + // If the path was referred to by using a -l argument, let's search + // for the file in the search path. + if (dashL) { + ErrorOr<StringRef> pathOrErr = ctx.searchLibrary(path); + if (std::error_code ec = pathOrErr.getError()) + return make_dynamic_error_code( + Twine("Unable to find library -l") + path + ": " + ec.message()); + path = pathOrErr.get(); + } + if (!llvm::sys::fs::exists(path)) + return make_dynamic_error_code( + Twine("lld: cannot find file ") + path); + return path; +} + +static bool isPathUnderSysroot(StringRef sysroot, StringRef path) { + if (sysroot.empty()) + return false; + while (!path.empty() && !llvm::sys::fs::equivalent(sysroot, path)) + path = llvm::sys::path::parent_path(path); + return !path.empty(); +} + +static std::error_code +addFilesFromLinkerScript(ELFLinkingContext &ctx, StringRef scriptPath, + const std::vector<script::Path> &inputPaths, + raw_ostream &diag) { + bool sysroot = (!ctx.getSysroot().empty() + && isPathUnderSysroot(ctx.getSysroot(), scriptPath)); + for (const script::Path &path : inputPaths) { + ErrorOr<StringRef> pathOrErr = path._isDashlPrefix + ? ctx.searchLibrary(path._path) : ctx.searchFile(path._path, sysroot); + if (std::error_code ec = pathOrErr.getError()) { + auto file = llvm::make_unique<ErrorFile>(path._path, ec); + ctx.getNodes().push_back(llvm::make_unique<FileNode>(std::move(file))); + continue; + } + + std::vector<std::unique_ptr<File>> files + = loadFile(ctx, pathOrErr.get(), false); + for (std::unique_ptr<File> &file : files) { + if (ctx.logInputFiles()) + diag << file->path() << "\n"; + ctx.getNodes().push_back(llvm::make_unique<FileNode>(std::move(file))); + } + } + return std::error_code(); +} + +std::error_code GnuLdDriver::evalLinkerScript(ELFLinkingContext &ctx, + std::unique_ptr<MemoryBuffer> mb, + raw_ostream &diag, + bool nostdlib) { + // Read the script file from disk and parse. + StringRef path = mb->getBufferIdentifier(); + auto parser = llvm::make_unique<script::Parser>(std::move(mb)); + if (std::error_code ec = parser->parse()) + return ec; + script::LinkerScript *script = parser->get(); + if (!script) + return LinkerScriptReaderError::parse_error; + // Evaluate script commands. + // Currently we only recognize this subset of linker script commands. + for (const script::Command *c : script->_commands) { + if (auto *input = dyn_cast<script::Input>(c)) + if (std::error_code ec = addFilesFromLinkerScript( + ctx, path, input->getPaths(), diag)) + return ec; + if (auto *group = dyn_cast<script::Group>(c)) { + int origSize = ctx.getNodes().size(); + if (std::error_code ec = addFilesFromLinkerScript( + ctx, path, group->getPaths(), diag)) + return ec; + size_t groupSize = ctx.getNodes().size() - origSize; + ctx.getNodes().push_back(llvm::make_unique<GroupEnd>(groupSize)); + } + if (auto *searchDir = dyn_cast<script::SearchDir>(c)) + if (!nostdlib) + ctx.addSearchPath(searchDir->getSearchPath()); + if (auto *entry = dyn_cast<script::Entry>(c)) + ctx.setEntrySymbolName(entry->getEntryName()); + if (auto *output = dyn_cast<script::Output>(c)) + ctx.setOutputPath(output->getOutputFileName()); + if (auto *externs = dyn_cast<script::Extern>(c)) { + for (auto symbol : *externs) { + ctx.addInitialUndefinedSymbol(symbol); + } + } + } + // Transfer ownership of the script to the linking context + ctx.linkerScriptSema().addLinkerScript(std::move(parser)); + return std::error_code(); +} + +bool GnuLdDriver::applyEmulation(llvm::Triple &triple, + llvm::opt::InputArgList &args, + raw_ostream &diag) { + llvm::opt::Arg *arg = args.getLastArg(OPT_m); + if (!arg) + return true; + llvm::Optional<llvm::Triple::ArchType> arch = + getArchType(triple, arg->getValue()); + if (!arch) { + diag << "error: unsupported emulation '" << arg->getValue() << "'.\n"; + return false; + } + triple.setArch(*arch); + return true; +} + +void GnuLdDriver::addPlatformSearchDirs(ELFLinkingContext &ctx, + llvm::Triple &triple, + llvm::Triple &baseTriple) { + if (triple.getOS() == llvm::Triple::NetBSD && + triple.getArch() == llvm::Triple::x86 && + baseTriple.getArch() == llvm::Triple::x86_64) { + ctx.addSearchPath("=/usr/lib/i386"); + return; + } + ctx.addSearchPath("=/usr/lib"); +} + +std::unique_ptr<ELFLinkingContext> +GnuLdDriver::createELFLinkingContext(llvm::Triple triple) { + std::unique_ptr<ELFLinkingContext> p; + // FIXME: #include "llvm/Config/Targets.def" +#define LLVM_TARGET(targetName) \ + if ((p = elf::targetName##LinkingContext::create(triple))) return p; + LLVM_TARGET(AArch64) + LLVM_TARGET(ARM) + LLVM_TARGET(Hexagon) + LLVM_TARGET(Mips) + LLVM_TARGET(X86) + LLVM_TARGET(Example) + LLVM_TARGET(X86_64) +#undef LLVM_TARGET + return nullptr; +} + +static llvm::Optional<bool> +getBool(const llvm::opt::InputArgList &parsedArgs, + unsigned yesFlag, unsigned noFlag) { + if (auto *arg = parsedArgs.getLastArg(yesFlag, noFlag)) + return arg->getOption().getID() == yesFlag; + return llvm::None; +} + +bool GnuLdDriver::parse(int argc, const char *argv[], + std::unique_ptr<ELFLinkingContext> &context, + raw_ostream &diag) { + // Parse command line options using GnuLdOptions.td + std::unique_ptr<llvm::opt::InputArgList> parsedArgs; + GnuLdOptTable table; + unsigned missingIndex; + unsigned missingCount; + + parsedArgs.reset( + table.ParseArgs(&argv[1], &argv[argc], missingIndex, missingCount)); + if (missingCount) { + diag << "error: missing arg value for '" + << parsedArgs->getArgString(missingIndex) << "' expected " + << missingCount << " argument(s).\n"; + return false; + } + + // Handle --help + if (parsedArgs->hasArg(OPT_help)) { + table.PrintHelp(llvm::outs(), argv[0], "LLVM Linker", false); + return true; + } + + // Use -target or use default target triple to instantiate LinkingContext + llvm::Triple baseTriple; + if (auto *arg = parsedArgs->getLastArg(OPT_target)) { + baseTriple = llvm::Triple(arg->getValue()); + } else { + baseTriple = getDefaultTarget(argv[0]); + } + llvm::Triple triple(baseTriple); + + if (!applyEmulation(triple, *parsedArgs, diag)) + return false; + + std::unique_ptr<ELFLinkingContext> ctx(createELFLinkingContext(triple)); + + if (!ctx) { + diag << "unknown target triple\n"; + return false; + } + + // Copy mllvm + for (auto *arg : parsedArgs->filtered(OPT_mllvm)) + ctx->appendLLVMOption(arg->getValue()); + + // Ignore unknown arguments. + for (auto unknownArg : parsedArgs->filtered(OPT_UNKNOWN)) + diag << "warning: ignoring unknown argument: " + << unknownArg->getValue() << "\n"; + + // Set sys root path. + if (auto *arg = parsedArgs->getLastArg(OPT_sysroot)) + ctx->setSysroot(arg->getValue()); + + // Handle --demangle option(For compatibility) + if (parsedArgs->hasArg(OPT_demangle)) + ctx->setDemangleSymbols(true); + + // Handle --no-demangle option. + if (parsedArgs->hasArg(OPT_no_demangle)) + ctx->setDemangleSymbols(false); + + // Figure out output kind (-r, -static, -shared) + if (parsedArgs->hasArg(OPT_relocatable)) { + ctx->setOutputELFType(llvm::ELF::ET_REL); + ctx->setPrintRemainingUndefines(false); + ctx->setAllowRemainingUndefines(true); + } + + if (parsedArgs->hasArg(OPT_static)) { + ctx->setOutputELFType(llvm::ELF::ET_EXEC); + ctx->setIsStaticExecutable(true); + } + + if (parsedArgs->hasArg(OPT_shared)) { + ctx->setOutputELFType(llvm::ELF::ET_DYN); + ctx->setAllowShlibUndefines(true); + ctx->setUseShlibUndefines(false); + ctx->setPrintRemainingUndefines(false); + ctx->setAllowRemainingUndefines(true); + } + + // Handle --stats. + if (parsedArgs->hasArg(OPT_stats)) { + ctx->setCollectStats(true); + } + + // Figure out if the output type is nmagic/omagic + if (auto *arg = parsedArgs->getLastArg( + OPT_nmagic, OPT_omagic, OPT_no_omagic)) { + switch (arg->getOption().getID()) { + case OPT_nmagic: + ctx->setOutputMagic(ELFLinkingContext::OutputMagic::NMAGIC); + ctx->setIsStaticExecutable(true); + break; + case OPT_omagic: + ctx->setOutputMagic(ELFLinkingContext::OutputMagic::OMAGIC); + ctx->setIsStaticExecutable(true); + break; + case OPT_no_omagic: + ctx->setOutputMagic(ELFLinkingContext::OutputMagic::DEFAULT); + ctx->setNoAllowDynamicLibraries(); + break; + } + } + + if (parsedArgs->hasArg(OPT_strip_all)) + ctx->setStripSymbols(true); + + if (auto *arg = parsedArgs->getLastArg(OPT_soname)) + ctx->setSharedObjectName(arg->getValue()); + + if (parsedArgs->hasArg(OPT_rosegment)) + ctx->setCreateSeparateROSegment(); + + if (parsedArgs->hasArg(OPT_no_align_segments)) + ctx->setAlignSegments(false); + + if (auto *arg = parsedArgs->getLastArg(OPT_image_base)) { + uint64_t baseAddress = 0; + StringRef inputValue = arg->getValue(); + if (inputValue.getAsInteger(0, baseAddress) || !baseAddress) { + diag << "invalid value for image base " << inputValue << "\n"; + return false; + } + ctx->setBaseAddress(baseAddress); + } + + if (parsedArgs->hasArg(OPT_merge_strings)) + ctx->setMergeCommonStrings(true); + + if (parsedArgs->hasArg(OPT_t)) + ctx->setLogInputFiles(true); + + if (parsedArgs->hasArg(OPT_use_shlib_undefs)) + ctx->setUseShlibUndefines(true); + + if (auto val = getBool(*parsedArgs, OPT_allow_shlib_undefs, + OPT_no_allow_shlib_undefs)) + ctx->setAllowShlibUndefines(*val); + + if (auto *arg = parsedArgs->getLastArg(OPT_e)) + ctx->setEntrySymbolName(arg->getValue()); + + if (auto *arg = parsedArgs->getLastArg(OPT_output)) + ctx->setOutputPath(arg->getValue()); + + if (parsedArgs->hasArg(OPT_noinhibit_exec)) + ctx->setAllowRemainingUndefines(true); + + if (auto val = getBool(*parsedArgs, OPT_export_dynamic, + OPT_no_export_dynamic)) + ctx->setExportDynamic(*val); + + if (parsedArgs->hasArg(OPT_allow_multiple_definition)) + ctx->setAllowDuplicates(true); + + if (auto *arg = parsedArgs->getLastArg(OPT_dynamic_linker)) + ctx->setInterpreter(arg->getValue()); + + if (auto *arg = parsedArgs->getLastArg(OPT_init)) + ctx->setInitFunction(arg->getValue()); + + if (auto *arg = parsedArgs->getLastArg(OPT_fini)) + ctx->setFiniFunction(arg->getValue()); + + if (auto *arg = parsedArgs->getLastArg(OPT_output_filetype)) + ctx->setOutputFileType(arg->getValue()); + + for (auto *arg : parsedArgs->filtered(OPT_L)) + ctx->addSearchPath(arg->getValue()); + + // Add the default search directory specific to the target. + if (!parsedArgs->hasArg(OPT_nostdlib)) + addPlatformSearchDirs(*ctx, triple, baseTriple); + + for (auto *arg : parsedArgs->filtered(OPT_u)) + ctx->addInitialUndefinedSymbol(arg->getValue()); + + for (auto *arg : parsedArgs->filtered(OPT_defsym)) { + StringRef sym, target; + uint64_t addr; + if (parseDefsymAsAbsolute(arg->getValue(), sym, addr)) { + ctx->addInitialAbsoluteSymbol(sym, addr); + } else if (parseDefsymAsAlias(arg->getValue(), sym, target)) { + ctx->addAlias(sym, target); + } else { + diag << "invalid --defsym: " << arg->getValue() << "\n"; + return false; + } + } + + for (auto *arg : parsedArgs->filtered(OPT_z)) { + StringRef opt = arg->getValue(); + if (opt == "muldefs") { + ctx->setAllowDuplicates(true); + } else if (opt.startswith("max-page-size")) { + // Parse -z max-page-size option. + // The default page size is considered the minimum page size the user + // can set, check the user input if its atleast the minimum page size + // and does not exceed the maximum page size allowed for the target. + uint64_t maxPageSize = 0; + + // Error if the page size user set is less than the maximum page size + // and greather than the default page size and the user page size is a + // modulo of the default page size. + if ((!parseMaxPageSize(opt, maxPageSize)) || + (maxPageSize < ctx->getPageSize()) || + (maxPageSize % ctx->getPageSize())) { + diag << "invalid option: " << opt << "\n"; + return false; + } + ctx->setMaxPageSize(maxPageSize); + } else { + diag << "warning: ignoring unknown argument for -z: " << opt << "\n"; + } + } + + for (auto *arg : parsedArgs->filtered(OPT_rpath)) { + SmallVector<StringRef, 2> rpaths; + StringRef(arg->getValue()).split(rpaths, ":"); + for (auto path : rpaths) + ctx->addRpath(path); + } + + for (auto *arg : parsedArgs->filtered(OPT_rpath_link)) { + SmallVector<StringRef, 2> rpaths; + StringRef(arg->getValue()).split(rpaths, ":"); + for (auto path : rpaths) + ctx->addRpathLink(path); + } + + // Support --wrap option. + for (auto *arg : parsedArgs->filtered(OPT_wrap)) + ctx->addWrapForSymbol(arg->getValue()); + + // Register possible input file parsers. + ctx->registry().addSupportELFObjects(*ctx); + ctx->registry().addSupportArchives(ctx->logInputFiles()); + ctx->registry().addSupportYamlFiles(); + ctx->registry().addSupportNativeObjects(); + if (ctx->allowLinkWithDynamicLibraries()) + ctx->registry().addSupportELFDynamicSharedObjects(*ctx); + + std::stack<int> groupStack; + int numfiles = 0; + bool asNeeded = false; + bool wholeArchive = false; + + // Process files + for (auto arg : *parsedArgs) { + switch (arg->getOption().getID()) { + case OPT_no_whole_archive: + wholeArchive = false; + break; + + case OPT_whole_archive: + wholeArchive = true; + break; + + case OPT_as_needed: + asNeeded = true; + break; + + case OPT_no_as_needed: + asNeeded = false; + break; + + case OPT_start_group: + groupStack.push(numfiles); + break; + + case OPT_end_group: { + if (groupStack.empty()) { + diag << "stray --end-group\n"; + return false; + } + int startGroupPos = groupStack.top(); + ctx->getNodes().push_back( + llvm::make_unique<GroupEnd>(numfiles - startGroupPos)); + groupStack.pop(); + break; + } + + case OPT_INPUT: + case OPT_l: + case OPT_T: { + bool dashL = (arg->getOption().getID() == OPT_l); + StringRef path = arg->getValue(); + + ErrorOr<StringRef> pathOrErr = findFile(*ctx, path, dashL); + if (std::error_code ec = pathOrErr.getError()) { + auto file = llvm::make_unique<ErrorFile>(path, ec); + auto node = llvm::make_unique<FileNode>(std::move(file)); + node->setAsNeeded(asNeeded); + ctx->getNodes().push_back(std::move(node)); + break; + } + StringRef realpath = pathOrErr.get(); + + bool isScript = + (!path.endswith(".objtxt") && isLinkerScript(realpath, diag)); + if (isScript) { + if (ctx->logInputFiles()) + diag << path << "\n"; + ErrorOr<std::unique_ptr<MemoryBuffer>> mb = + MemoryBuffer::getFileOrSTDIN(realpath); + if (std::error_code ec = mb.getError()) { + diag << "Cannot open " << path << ": " << ec.message() << "\n"; + return false; + } + bool nostdlib = parsedArgs->hasArg(OPT_nostdlib); + std::error_code ec = + evalLinkerScript(*ctx, std::move(mb.get()), diag, nostdlib); + if (ec) { + diag << path << ": Error parsing linker script: " + << ec.message() << "\n"; + return false; + } + break; + } + std::vector<std::unique_ptr<File>> files + = loadFile(*ctx, realpath, wholeArchive); + for (std::unique_ptr<File> &file : files) { + if (ctx->logInputFiles()) + diag << file->path() << "\n"; + auto node = llvm::make_unique<FileNode>(std::move(file)); + node->setAsNeeded(asNeeded); + ctx->getNodes().push_back(std::move(node)); + } + numfiles += files.size(); + break; + } + } + } + + if (ctx->getNodes().empty()) { + diag << "No input files\n"; + return false; + } + + // Set default output file name if the output file was not specified. + if (ctx->outputPath().empty()) { + switch (ctx->outputFileType()) { + case LinkingContext::OutputFileType::YAML: + ctx->setOutputPath("-"); + break; + case LinkingContext::OutputFileType::Native: + ctx->setOutputPath("a.native"); + break; + default: + ctx->setOutputPath("a.out"); + break; + } + } + + // Validate the combination of options used. + if (!ctx->validate(diag)) + return false; + + // Perform linker script semantic actions + ctx->linkerScriptSema().perform(); + + context.swap(ctx); + return true; +} + +/// Get the default target triple based on either the program name +/// (e.g. "x86-ibm-linux-lld") or the primary target llvm was configured for. +llvm::Triple GnuLdDriver::getDefaultTarget(const char *progName) { + SmallVector<StringRef, 4> components; + llvm::SplitString(llvm::sys::path::stem(progName), components, "-"); + // If has enough parts to be start with a triple. + if (components.size() >= 4) { + llvm::Triple triple(components[0], components[1], components[2], + components[3]); + // If first component looks like an arch. + if (triple.getArch() != llvm::Triple::UnknownArch) + return triple; + } + + // Fallback to use whatever default triple llvm was configured for. + return llvm::Triple(llvm::sys::getDefaultTargetTriple()); +} diff --git a/lib/Driver/GnuLdOptions.td b/lib/Driver/GnuLdOptions.td new file mode 100644 index 0000000000000..9d06f29354395 --- /dev/null +++ b/lib/Driver/GnuLdOptions.td @@ -0,0 +1,323 @@ +include "llvm/Option/OptParser.td" + +//===----------------------------------------------------------------------===// +/// Utility Functions +//===----------------------------------------------------------------------===// +// Single and multiple dash options combined +multiclass smDash<string opt1, string opt2, string help> { + // Option + def "" : Separate<["-"], opt1>, HelpText<help>; + def opt1_eq : Joined<["-"], opt1#"=">, + Alias<!cast<Option>(opt1)>; + // Compatibility aliases + def opt2_dashdash : Separate<["--"], opt2>, + Alias<!cast<Option>(opt1)>; + def opt2_dashdash_eq : Joined<["--"], opt2#"=">, + Alias<!cast<Option>(opt1)>; +} + +// Support -<option>,-<option>= +multiclass dashEq<string opt1, string opt2, string help> { + // Option + def "" : Separate<["-"], opt1>, HelpText<help>; + // Compatibility aliases + def opt2_eq : Joined<["-"], opt2#"=">, + Alias<!cast<Option>(opt1)>; +} + +//===----------------------------------------------------------------------===// +/// LLVM and Target options +//===----------------------------------------------------------------------===// +def grp_llvmtarget : OptionGroup<"opts">, + HelpText<"LLVM and Target Options">; +def mllvm : Separate<["-"], "mllvm">, + HelpText<"Options to pass to LLVM">, Group<grp_llvmtarget>; +def target : Separate<["-"], "target">, MetaVarName<"<triple>">, + HelpText<"Target triple to link for">, + Group<grp_llvmtarget>; + +//===----------------------------------------------------------------------===// +/// Output Kinds +//===----------------------------------------------------------------------===// +def grp_kind : OptionGroup<"outs">, + HelpText<"OUTPUT KIND">; +def relocatable : Flag<["-"], "r">, + HelpText<"Create relocatable object file">, Group<grp_kind>; +def static : Flag<["-"], "static">, + HelpText<"Create static executable">, Group<grp_kind>; +def dynamic : Flag<["-"], "dynamic">, + HelpText<"Create dynamic executable (default)">,Group<grp_kind>; +def shared : Flag<["-"], "shared">, + HelpText<"Create dynamic library">, Group<grp_kind>; + +// output kinds - compatibility aliases +def Bstatic : Flag<["-"], "Bstatic">, Alias<static>; +def Bshareable : Flag<["-"], "Bshareable">, Alias<shared>; + +//===----------------------------------------------------------------------===// +/// General Options +//===----------------------------------------------------------------------===// +def grp_general : OptionGroup<"opts">, + HelpText<"GENERAL OPTIONS">; +def output : Separate<["-"], "o">, MetaVarName<"<path>">, + HelpText<"Path to file to write output">, + Group<grp_general>; +def m : Separate<["-"], "m">, MetaVarName<"<emulation>">, + HelpText<"Select target emulation">, + Group<grp_general>; +def build_id : Flag<["--"], "build-id">, + HelpText<"Request creation of \".note.gnu.build-id\" ELF note section">, + Group<grp_general>; +def sysroot : Joined<["--"], "sysroot=">, + HelpText<"Set the system root">, + Group<grp_general>; + + +//===----------------------------------------------------------------------===// +/// Executable Options +//===----------------------------------------------------------------------===// +def grp_main : OptionGroup<"opts">, + HelpText<"EXECUTABLE OPTIONS">; +def L : Joined<["-"], "L">, MetaVarName<"<dir>">, + HelpText<"Directory to search for libraries">, + Group<grp_main>; +def l : Joined<["-"], "l">, MetaVarName<"<libName>">, + HelpText<"Root name of library to use">, + Group<grp_main>; +def noinhibit_exec : Flag<["--"], "noinhibit-exec">, + HelpText<"Retain the executable output file whenever" + " it is still usable">, + Group<grp_main>; +defm e : smDash<"e", "entry", + "Name of entry point symbol">, + Group<grp_main>; +defm init: dashEq<"init", "init", + "Specify an initializer function">, + Group<grp_main>; +defm fini: dashEq<"fini", "fini", + "Specify a finalizer function">, + Group<grp_main>; +def whole_archive: Flag<["--"], "whole-archive">, + HelpText<"Force load of all members in a static library">, + Group<grp_main>; +def no_whole_archive: Flag<["--"], "no-whole-archive">, + HelpText<"Restores the default behavior of loading archive members">, + Group<grp_main>; +def nostdlib : Flag<["-"], "nostdlib">, + HelpText<"Disable default search path for libraries">, + Group<grp_main>; +def image_base : Separate<["--"], "image-base">, + HelpText<"Set the base address">, + Group<grp_main>; + +//===----------------------------------------------------------------------===// +/// Static Executable Options +//===----------------------------------------------------------------------===// +def grp_staticexec : OptionGroup<"opts">, + HelpText<"STATIC EXECUTABLE OPTIONS">; +def nmagic : Flag<["--"], "nmagic">, + HelpText<"Turn off page alignment of sections," + " and disable linking against shared libraries">, + Group<grp_staticexec>; +def omagic : Flag<["--"], "omagic">, + HelpText<"Set the text and data sections to be readable and writable." + " Also, do not page-align the data segment, and" + " disable linking against shared libraries.">, + Group<grp_staticexec>; +def no_omagic : Flag<["--"], "no-omagic">, + HelpText<"This option negates most of the effects of the -N option." + "Disable linking with shared libraries">, + Group<grp_staticexec>; +// Compatible Aliases +def nmagic_alias : Flag<["-"], "n">, + Alias<nmagic>; +def omagic_alias : Flag<["-"], "N">, + Alias<omagic>; + +//===----------------------------------------------------------------------===// +/// Dynamic Library/Executable Options +//===----------------------------------------------------------------------===// +def grp_dynlibexec : OptionGroup<"opts">, + HelpText<"DYNAMIC LIBRARY/EXECUTABLE OPTIONS">; +def dynamic_linker : Joined<["--"], "dynamic-linker=">, + HelpText<"Set the path to the dynamic linker">, Group<grp_dynlibexec>; +// Executable options - compatibility aliases +def dynamic_linker_alias : Separate<["-"], "dynamic-linker">, + Alias<dynamic_linker>; +defm rpath : dashEq<"rpath", "rpath", + "Add a directory to the runtime library search path">, + Group<grp_dynlibexec>; +def rpath_link : Separate<["-"], "rpath-link">, + HelpText<"Specifies the first set of directories to search">, + Group<grp_dynlibexec>; +def export_dynamic : Flag<["-", "--"], "export-dynamic">, + HelpText<"Add all symbols to the dynamic symbol table" + " when creating executables">, + Group<grp_main>; +def alias_export_dynamic: Flag<["-"], "E">, + Alias<export_dynamic>; +def no_export_dynamic : Flag<["--"], "no-export-dynamic">, + Group<grp_main>; + +//===----------------------------------------------------------------------===// +/// Dynamic Library Options +//===----------------------------------------------------------------------===// +def grp_dynlib : OptionGroup<"opts">, + HelpText<"DYNAMIC LIBRARY OPTIONS">; +def soname : Joined<["-", "--"], "soname=">, + HelpText<"Set the internal DT_SONAME field to the specified name">, + Group<grp_dynlib>; +def soname_separate : Separate<["-", "--"], "soname">, Alias<soname>; +def soname_h : Separate<["-"], "h">, Alias<soname>; + +//===----------------------------------------------------------------------===// +/// Resolver Options +//===----------------------------------------------------------------------===// +def grp_resolveropt : OptionGroup<"opts">, + HelpText<"SYMBOL RESOLUTION OPTIONS">; +defm u : smDash<"u", "undefined", + "Force symbol to be entered in the output file" + " as an undefined symbol">, + Group<grp_resolveropt>; +def start_group : Flag<["--"], "start-group">, + HelpText<"Start a group">, + Group<grp_resolveropt>; +def alias_start_group: Flag<["-"], "(">, + Alias<start_group>; +def end_group : Flag<["--"], "end-group">, + HelpText<"End a group">, + Group<grp_resolveropt>; +def alias_end_group: Flag<["-"], ")">, + Alias<end_group>; +def as_needed : Flag<["--"], "as-needed">, + HelpText<"This option affects ELF DT_NEEDED tags for " + "dynamic libraries mentioned on the command line">, + Group<grp_resolveropt>; +def no_as_needed : Flag<["--"], "no-as-needed">, + HelpText<"This option restores the default behavior" + " of adding DT_NEEDED entries">, + Group<grp_resolveropt>; +def no_allow_shlib_undefs : Flag<["--"], "no-allow-shlib-undefined">, + HelpText<"Do not allow undefined symbols from dynamic" + " library when creating executables">, + Group<grp_resolveropt>; +def allow_shlib_undefs : Flag<["-", "--"], "allow-shlib-undefined">, + HelpText<"Allow undefined symbols from dynamic" + " library when creating executables">, + Group<grp_resolveropt>; +def use_shlib_undefs: Flag<["--"], "use-shlib-undefines">, + HelpText<"Resolve undefined symbols from dynamic libraries">, + Group<grp_resolveropt>; +def allow_multiple_definition: Flag<["--"], "allow-multiple-definition">, + HelpText<"Allow multiple definitions">, + Group<grp_resolveropt>; +def defsym : Joined<["--"], "defsym=">, + HelpText<"Create a defined symbol">, + Group<grp_resolveropt>; + +//===----------------------------------------------------------------------===// +/// Custom Options +//===----------------------------------------------------------------------===// +def grp_customopts : OptionGroup<"opts">, + HelpText<"CUSTOM OPTIONS">; +def rosegment: Flag<["--"], "rosegment">, + HelpText<"Put read-only non-executable sections in their own segment">, + Group<grp_customopts>; +def z : Separate<["-"], "z">, + HelpText<"Linker Option extensions">, + Group<grp_customopts>; +def no_align_segments: Flag<["--"], "no-align-segments">, + HelpText<"Don't align ELF segments(virtualaddress/fileoffset) to page boundaries">, + Group<grp_customopts>; + +//===----------------------------------------------------------------------===// +/// Symbol options +//===----------------------------------------------------------------------===// +def grp_symbolopts : OptionGroup<"opts">, + HelpText<"SYMBOL OPTIONS">; +def demangle : Flag<["--"], "demangle">, + HelpText<"Demangle C++ symbols">, + Group<grp_symbolopts>; +def no_demangle : Flag<["--"], "no-demangle">, + HelpText<"Dont demangle C++ symbols">, + Group<grp_symbolopts>; +def strip_all : Flag<["--"], "strip-all">, + HelpText<"Omit all symbol informations from output">, + Group<grp_symbolopts>; +def alias_strip_all : Flag<["-"], "s">, + Alias<strip_all>; +defm wrap : smDash<"wrap", "wrap", + "Use a wrapper function for symbol. Any " + " undefined reference to symbol will be resolved to " + "\"__wrap_symbol\". Any undefined reference to \"__real_symbol\"" + " will be resolved to symbol.">, + MetaVarName<"<symbol>">, + Group<grp_symbolopts>; + +//===----------------------------------------------------------------------===// +/// Script Options +//===----------------------------------------------------------------------===// +def grp_scriptopts : OptionGroup<"opts">, + HelpText<"SCRIPT OPTIONS">; +defm T : smDash<"T", "script", + "Use the given linker script in place of the default script.">, + Group<grp_scriptopts>; + +//===----------------------------------------------------------------------===// +/// Optimization Options +//===----------------------------------------------------------------------===// +def grp_opts : OptionGroup<"opts">, + HelpText<"OPTIMIZATIONS">; +def hash_style : Joined <["--"], "hash-style=">, + HelpText<"Set the type of linker's hash table(s)">, + Group<grp_opts>; +def merge_strings : Flag<["--"], "merge-strings">, + HelpText<"Merge common strings across mergeable sections">, + Group<grp_opts>; +def eh_frame_hdr : Flag<["--"], "eh-frame-hdr">, + HelpText<"Request creation of .eh_frame_hdr section and ELF " + " PT_GNU_EH_FRAME segment header">, + Group<grp_opts>; + +//===----------------------------------------------------------------------===// +/// Tracing Options +//===----------------------------------------------------------------------===// +def grp_tracingopts : OptionGroup<"opts">, + HelpText<"TRACING OPTIONS">; +def t : Flag<["-"], "t">, + HelpText<"Print the names of the input files as ld processes them">, + Group<grp_tracingopts>; +def stats : Flag<["--"], "stats">, + HelpText<"Print time and memory usage stats">, Group<grp_tracingopts>; + +//===----------------------------------------------------------------------===// +/// Extensions +//===----------------------------------------------------------------------===// +def grp_extns : OptionGroup<"opts">, + HelpText<"Extensions">; +def output_filetype: Separate<["--"], "output-filetype">, + HelpText<"Specify what type of output file that lld creates, YAML/Native">, + Group<grp_extns>; +def alias_output_filetype: Joined<["--"], "output-filetype=">, + Alias<output_filetype>; + +//===----------------------------------------------------------------------===// +/// Ignored options +//===----------------------------------------------------------------------===// +def grp_ignored: OptionGroup<"ignored">, + HelpText<"GNU Options ignored for Compatibility ">; +def dashg : Flag<["-"], "g">, + HelpText<"Ignored.">, + Group<grp_ignored>; +def Qy : Flag<["-"], "Qy">, + HelpText<"Ignored for SVR4 Compatibility">, + Group<grp_ignored>; +def qmagic : Flag<["-"], "qmagic">, + HelpText<"Ignored for Linux Compatibility">, + Group<grp_ignored>; + +//===----------------------------------------------------------------------===// +/// Help +//===----------------------------------------------------------------------===// +def help : Flag<["--"], "help">, + HelpText<"Display this help message">; diff --git a/lib/Driver/Makefile b/lib/Driver/Makefile new file mode 100644 index 0000000000000..19024cfab0f16 --- /dev/null +++ b/lib/Driver/Makefile @@ -0,0 +1,38 @@ +##===- lld/lib/Driver/Makefile ---------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +LLD_LEVEL := ../.. +LIBRARYNAME := lldDriver + +BUILT_SOURCES = CoreOptions.inc UniversalDriverOptions.inc DarwinLdOptions.inc \ + GnuLdOptions.inc WinLinkOptions.inc + +TABLEGEN_INC_FILES_COMMON = 1 + +include $(LLD_LEVEL)/Makefile + +$(ObjDir)/CoreOptions.inc.tmp : CoreOptions.td $(LLVM_TBLGEN) $(ObjDir)/.dir + $(Echo) "Building LLD CoreOptions Option tables with tblgen" + $(Verb) $(LLVMTableGen) -gen-opt-parser-defs -o $(call SYSPATH, $@) $< + +$(ObjDir)/UniversalDriverOptions.inc.tmp : UniversalDriverOptions.td $(LLVM_TBLGEN) $(ObjDir)/.dir + $(Echo) "Building LLD Universal Driver Options tables with tblgen" + $(Verb) $(LLVMTableGen) -gen-opt-parser-defs -o $(call SYSPATH, $@) $< + +$(ObjDir)/DarwinLdOptions.inc.tmp : DarwinLdOptions.td $(LLVM_TBLGEN) $(ObjDir)/.dir + $(Echo) "Building LLD Darwin ld Option tables with tblgen" + $(Verb) $(LLVMTableGen) -gen-opt-parser-defs -o $(call SYSPATH, $@) $< + +$(ObjDir)/GnuLdOptions.inc.tmp : GnuLdOptions.td $(LLVM_TBLGEN) $(ObjDir)/.dir + $(Echo) "Building LLD Gnu ld Option tables with tblgen" + $(Verb) $(LLVMTableGen) -gen-opt-parser-defs -o $(call SYSPATH, $@) $< + +$(ObjDir)/WinLinkOptions.inc.tmp : WinLinkOptions.td $(LLVM_TBLGEN) $(ObjDir)/.dir + $(Echo) "Building LLD WinLinkOptions Option tables with tblgen" + $(Verb) $(LLVMTableGen) -gen-opt-parser-defs -o $(call SYSPATH, $@) $< diff --git a/lib/Driver/TODO.rst b/lib/Driver/TODO.rst new file mode 100644 index 0000000000000..e03d829c232dd --- /dev/null +++ b/lib/Driver/TODO.rst @@ -0,0 +1,101 @@ +GNU ld Driver +~~~~~~~~~~~~~ + +Missing Options +############### + +* --audit +* -A,--architecture +* -b,--format +* -d,-dc,-dp +* -P,--depaudit +* --exclude-libs +* --exclude-modules-for-implib +* -E,--export-dynamic,--no-export-dynamic +* -EB (We probably shouldn't support this) +* -EL (We probably shouldn't support this) +* -f,--auxiliary +* -F,--filter +* -G,--gpsize +* -h +* -i +* --library +* -M +* --print-map +* -output +* -O +* -q,--emit-relocs +* --force-dynamic +* --relocatable +* -R,--just-symbols +* -s,--strip-all +* -S,--strip-debug +* --trace +* -dT,--default-script +* -Ur +* --unique +* -v,--version,-V +* -x,--discard-all +* -X,--discard-locals +* -y,--trace-symbol +* -z (keywords need to be implemented) +* --accept-unknown-input-arch,--no-accept-unknown-input-arch +* -Bdynamic,-dy,-call_shared +* -Bgroup +* -dn,-non_shared +* -Bsymbolic +* -Bsymbolic-functions +* --dynamic-list +* --dynamic-list-data +* --dynamic-list-cpp-new +* --dynamic-list-cpp-typeinfo +* --check-sections,--no-check-sections +* --copy-dt-needed-entries,--no-copy-dt-needed-entires +* --cref +* --no-define-common +* --defsym (only absolute value supported now) +* --demangle,--no-demangle +* -I +* --fatal-warnings,--no-fatal-warnings +* --force-exe-suffix +* --gc-sections,--no-gc-sections +* --print-gc-sections,--no-print-gc-sections +* --print-output-format +* --target-help +* -Map +* --no-keep-memory +* --no-undefined,-z defs +* --allow-shlib-undefined,--no-alow-shlib-undefined +* --no-undefined-version +* --default-symver +* --default-imported-symver +* --no-warn-mismatch +* --no-warn-search-mismatch +* --oformat +* -pie,--pic-executable +* --relax,--no-relax +* --retain-symbols-file +* --sort-common +* --sort-section={name,alignment} +* --split-by-file +* --split-by-reloc +* --stats +* --section-start +* -T{bss,data,text,{text,rodata,data}-segment} +* --unresolved-symbols +* -dll-verbose,--verbose +* --version-script +* --warn-common +* --warn-constructors +* --warn-multiple-gp +* --warn-once +* --warn-section-align +* --warn-shared-textrel +* --warn-alternate-em +* --warn-unresolved-symbols +* --error-unresolved-symbols +* --wrap +* --no-ld-generated-unwind-info +* --hash-size +* --reduce-memory-overheads +* --build-id diff --git a/lib/Driver/UniversalDriver.cpp b/lib/Driver/UniversalDriver.cpp new file mode 100644 index 0000000000000..7d42ad7b4bfc0 --- /dev/null +++ b/lib/Driver/UniversalDriver.cpp @@ -0,0 +1,218 @@ +//===- lib/Driver/UniversalDriver.cpp -------------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// +/// Driver for "universal" lld tool which can mimic any linker command line +/// parsing once it figures out which command line flavor to use. +/// +//===----------------------------------------------------------------------===// + +#include "lld/Driver/Driver.h" +#include "lld/Config/Version.h" +#include "lld/Core/LLVM.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/Option.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Host.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" + +using namespace lld; + +namespace { + +// Create enum with OPT_xxx values for each option in GnuLdOptions.td +enum { + OPT_INVALID = 0, +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELP, META) \ + OPT_##ID, +#include "UniversalDriverOptions.inc" +#undef OPTION +}; + +// Create prefix string literals used in GnuLdOptions.td +#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; +#include "UniversalDriverOptions.inc" +#undef PREFIX + +// Create table mapping all options defined in GnuLdOptions.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 "UniversalDriverOptions.inc" +#undef OPTION +}; + +// Create OptTable class for parsing actual command line arguments +class UniversalDriverOptTable : public llvm::opt::OptTable { +public: + UniversalDriverOptTable() + : OptTable(infoTable, llvm::array_lengthof(infoTable)) {} +}; + +enum class Flavor { + invalid, + gnu_ld, // -flavor gnu + win_link, // -flavor link + darwin_ld, // -flavor darwin + core // -flavor core OR -core +}; + +struct ProgramNameParts { + StringRef _target; + StringRef _flavor; +}; + +} // anonymous namespace + +static Flavor strToFlavor(StringRef str) { + return llvm::StringSwitch<Flavor>(str) + .Case("gnu", Flavor::gnu_ld) + .Case("link", Flavor::win_link) + .Case("lld-link", Flavor::win_link) + .Case("darwin", Flavor::darwin_ld) + .Case("core", Flavor::core) + .Case("ld", Flavor::gnu_ld) + .Default(Flavor::invalid); +} + +static ProgramNameParts parseProgramName(StringRef programName) { + SmallVector<StringRef, 3> components; + llvm::SplitString(programName, components, "-"); + ProgramNameParts ret; + + using std::begin; + using std::end; + + // Erase any lld components. + components.erase(std::remove(components.begin(), components.end(), "lld"), + components.end()); + + // Find the flavor component. + auto flIter = std::find_if(components.begin(), components.end(), + [](StringRef str) -> bool { + return strToFlavor(str) != Flavor::invalid; + }); + + if (flIter != components.end()) { + ret._flavor = *flIter; + components.erase(flIter); + } + + // Any remaining component must be the target. + if (components.size() == 1) + ret._target = components[0]; + + return ret; +} + +// Removes the argument from argv along with its value, if exists, and updates +// argc. +static void removeArg(llvm::opt::Arg *arg, int &argc, const char **&argv) { + unsigned int numToRemove = arg->getNumValues() + 1; + unsigned int argIndex = arg->getIndex() + 1; + + std::rotate(&argv[argIndex], &argv[argIndex + numToRemove], argv + argc); + argc -= numToRemove; +} + +static Flavor getFlavor(int &argc, const char **&argv, + std::unique_ptr<llvm::opt::InputArgList> &parsedArgs) { + if (llvm::opt::Arg *argCore = parsedArgs->getLastArg(OPT_core)) { + removeArg(argCore, argc, argv); + return Flavor::core; + } + if (llvm::opt::Arg *argFlavor = parsedArgs->getLastArg(OPT_flavor)) { + removeArg(argFlavor, argc, argv); + return strToFlavor(argFlavor->getValue()); + } + +#if LLVM_ON_UNIX + if (llvm::sys::path::filename(argv[0]).equals("ld")) { +#if __APPLE__ + // On a Darwin systems, if linker binary is named "ld", use Darwin driver. + return Flavor::darwin_ld; +#endif + // On a ELF based systems, if linker binary is named "ld", use gnu driver. + return Flavor::gnu_ld; + } +#endif + + StringRef name = llvm::sys::path::stem(argv[0]); + return strToFlavor(parseProgramName(name)._flavor); +} + +namespace lld { + +bool UniversalDriver::link(int argc, const char *argv[], + raw_ostream &diagnostics) { + // Parse command line options using GnuLdOptions.td + std::unique_ptr<llvm::opt::InputArgList> parsedArgs; + UniversalDriverOptTable table; + unsigned missingIndex; + unsigned missingCount; + + // Program name + StringRef programName = llvm::sys::path::stem(argv[0]); + + parsedArgs.reset( + table.ParseArgs(&argv[1], &argv[argc], missingIndex, missingCount)); + + if (missingCount) { + diagnostics << "error: missing arg value for '" + << parsedArgs->getArgString(missingIndex) << "' expected " + << missingCount << " argument(s).\n"; + return false; + } + + // Handle -help + if (parsedArgs->getLastArg(OPT_help)) { + table.PrintHelp(llvm::outs(), programName.data(), "LLVM Linker", false); + return true; + } + + // Handle -version + if (parsedArgs->getLastArg(OPT_version)) { + diagnostics << "LLVM Linker Version: " << getLLDVersion() + << getLLDRepositoryVersion() << "\n"; + return true; + } + + Flavor flavor = getFlavor(argc, argv, parsedArgs); + std::vector<const char *> args(argv, argv + argc); + + // Switch to appropriate driver. + switch (flavor) { + case Flavor::gnu_ld: + return GnuLdDriver::linkELF(args.size(), args.data(), diagnostics); + case Flavor::darwin_ld: + return DarwinLdDriver::linkMachO(args.size(), args.data(), diagnostics); + case Flavor::win_link: + return WinLinkDriver::linkPECOFF(args.size(), args.data(), diagnostics); + case Flavor::core: + return CoreDriver::link(args.size(), args.data(), diagnostics); + case Flavor::invalid: + diagnostics << "Select the appropriate flavor\n"; + table.PrintHelp(llvm::outs(), programName.data(), "LLVM Linker", false); + return false; + } + llvm_unreachable("Unrecognised flavor"); +} + +} // end namespace lld diff --git a/lib/Driver/UniversalDriverOptions.td b/lib/Driver/UniversalDriverOptions.td new file mode 100644 index 0000000000000..14abc9ce9911d --- /dev/null +++ b/lib/Driver/UniversalDriverOptions.td @@ -0,0 +1,19 @@ +include "llvm/Option/OptParser.td" + +// Select an optional flavor +def flavor: Separate<["-"], "flavor">, + HelpText<"Flavor for linking, options are gnu/darwin/link">; + +// Select the core flavor +def core : Flag<["-"], "core">, + HelpText<"CORE linking">; + +def target: Separate<["-"], "target">, + HelpText<"Select the target">; + +def version: Flag<["-"], "version">, + HelpText<"Display the version">; + +// Help message +def help : Flag<["-"], "help">, + HelpText<"Display this help message">; diff --git a/lib/Driver/WinLinkDriver.cpp b/lib/Driver/WinLinkDriver.cpp new file mode 100644 index 0000000000000..6ee7a5a004b5a --- /dev/null +++ b/lib/Driver/WinLinkDriver.cpp @@ -0,0 +1,1371 @@ +//===- 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 "lld/Driver/Driver.h" +#include "lld/Driver/WinLinkModuleDef.h" +#include "lld/ReaderWriter/PECOFFLinkingContext.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Object/COFF.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/Option.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Process.h" +#include "llvm/Support/Program.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <cctype> +#include <map> +#include <memory> +#include <sstream> +#include <tuple> + +namespace lld { + +// +// Option definitions +// + +// Create enum with OPT_xxx values for each option in WinLinkOptions.td +enum { + OPT_INVALID = 0, +#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 +}; + +namespace { + +// Create OptTable class for parsing actual command line arguments +class WinLinkOptTable : public llvm::opt::OptTable { +public: + // 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) {} +}; + +} // anonymous namespace + +// +// Functions to parse each command line option +// + +// Split the given string with spaces. +static 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. +static std::vector<StringRef> splitPathList(StringRef str) { + std::vector<StringRef> ret; + while (!str.empty()) { + StringRef path; + std::tie(path, str) = str.split(';'); + ret.push_back(path); + } + return ret; +} + +// Parse an argument for /alternatename. The expected string is +// "<string>=<string>". +static bool parseAlternateName(StringRef arg, StringRef &weak, StringRef &def, + raw_ostream &diag) { + std::tie(weak, def) = arg.split('='); + if (weak.empty() || def.empty()) { + diag << "Error: malformed /alternatename option: " << arg << "\n"; + return false; + } + return true; +} + +// Parse an argument for /base, /stack or /heap. The expected string +// is "<integer>[,<integer>]". +static bool parseMemoryOption(StringRef arg, uint64_t &reserve, + uint64_t &commit) { + StringRef reserveStr, commitStr; + std::tie(reserveStr, commitStr) = arg.split(','); + if (reserveStr.getAsInteger(0, reserve)) + return false; + if (!commitStr.empty() && commitStr.getAsInteger(0, commit)) + return false; + return true; +} + +// Parse an argument for /version or /subsystem. The expected string is +// "<integer>[.<integer>]". +static bool parseVersion(StringRef arg, uint32_t &major, uint32_t &minor) { + StringRef majorVersion, minorVersion; + std::tie(majorVersion, minorVersion) = arg.split('.'); + if (minorVersion.empty()) + minorVersion = "0"; + if (majorVersion.getAsInteger(0, major)) + return false; + if (minorVersion.getAsInteger(0, minor)) + return false; + return true; +} + +// Returns subsystem type for the given string. +static 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]]". +static bool parseSubsystem(StringRef arg, + llvm::COFF::WindowsSubsystem &subsystem, + llvm::Optional<uint32_t> &major, + llvm::Optional<uint32_t> &minor, raw_ostream &diag) { + StringRef subsystemStr, osVersion; + std::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) { + diag << "error: unknown subsystem name: " << subsystemStr << "\n"; + return false; + } + return true; +} + +static llvm::COFF::MachineTypes stringToMachineType(StringRef str) { + // FIXME: we have no way to differentiate between ARM and ARMNT currently. + // However, given that LLVM only supports ARM NT, default to that for now. + return llvm::StringSwitch<llvm::COFF::MachineTypes>(str.lower()) + .Case("arm", llvm::COFF::IMAGE_FILE_MACHINE_ARMNT) + .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. +static bool parseSection(StringRef option, std::string §ion, + llvm::Optional<uint32_t> &flags, + llvm::Optional<uint32_t> &mask) { + StringRef flagString; + std::tie(section, flagString) = option.split(","); + + bool negative = false; + if (flagString.startswith("!")) { + negative = true; + flagString = flagString.substr(1); + } + if (flagString.empty()) + return false; + + uint32_t attribs = 0; + for (size_t i = 0, e = flagString.size(); i < e; ++i) { + switch (tolower(flagString[i])) { +#define CASE(c, flag) \ + case c: \ + attribs |= flag; \ + break + CASE('d', llvm::COFF::IMAGE_SCN_MEM_DISCARDABLE); + CASE('e', llvm::COFF::IMAGE_SCN_MEM_EXECUTE); + CASE('k', llvm::COFF::IMAGE_SCN_MEM_NOT_CACHED); + CASE('p', llvm::COFF::IMAGE_SCN_MEM_NOT_PAGED); + CASE('r', llvm::COFF::IMAGE_SCN_MEM_READ); + CASE('s', llvm::COFF::IMAGE_SCN_MEM_SHARED); + CASE('w', llvm::COFF::IMAGE_SCN_MEM_WRITE); +#undef CASE + default: + return false; + } + } + + if (negative) { + mask = attribs; + } else { + flags = attribs; + } + return true; +} + +static bool readFile(PECOFFLinkingContext &ctx, StringRef path, + ArrayRef<uint8_t> &result) { + ErrorOr<std::unique_ptr<MemoryBuffer>> buf = MemoryBuffer::getFile(path); + if (!buf) + return false; + StringRef Data = buf.get()->getBuffer(); + result = ctx.allocate(ArrayRef<uint8_t>( + reinterpret_cast<const uint8_t *>(Data.begin()), Data.size())); + return true; +} + +// Parse /manifest:EMBED[,ID=#]|NO. +static bool parseManifest(StringRef option, bool &enable, bool &embed, + int &id) { + if (option.equals_lower("no")) { + enable = false; + return true; + } + if (!option.startswith_lower("embed")) + return false; + + embed = true; + option = option.substr(strlen("embed")); + if (option.empty()) + return true; + if (!option.startswith_lower(",id=")) + return false; + option = option.substr(strlen(",id=")); + if (option.getAsInteger(0, id)) + return false; + return true; +} + +static bool isLibraryFile(StringRef path) { + return path.endswith_lower(".lib") || path.endswith_lower(".imp"); +} + +static StringRef getObjectPath(PECOFFLinkingContext &ctx, StringRef path) { + std::string result; + if (isLibraryFile(path)) { + result = ctx.searchLibraryFile(path); + } else if (llvm::sys::path::extension(path).empty()) { + result = path.str() + ".obj"; + } else { + result = path; + } + return ctx.allocate(result); +} + +static StringRef getLibraryPath(PECOFFLinkingContext &ctx, StringRef path) { + std::string result = isLibraryFile(path) + ? ctx.searchLibraryFile(path) + : ctx.searchLibraryFile(path.str() + ".lib"); + return ctx.allocate(result); +} + +// Returns true if the given file is a Windows resource file. +static bool isResoruceFile(StringRef path) { + llvm::sys::fs::file_magic fileType; + if (llvm::sys::fs::identify_magic(path, fileType)) { + // If we cannot read the file, assume it's not a resource file. + // The further stage will raise an error on this unreadable file. + return false; + } + return fileType == llvm::sys::fs::file_magic::windows_resource; +} + +// Merge Windows resource files and convert them to a single COFF file. +// The temporary file path is set to result. +static bool convertResourceFiles(PECOFFLinkingContext &ctx, + std::vector<std::string> inFiles, + std::string &result) { + // Create an output file path. + SmallString<128> outFile; + if (llvm::sys::fs::createTemporaryFile("resource", "obj", outFile)) + return false; + std::string outFileArg = ("/out:" + outFile).str(); + + // Construct CVTRES.EXE command line and execute it. + std::string program = "cvtres.exe"; + ErrorOr<std::string> programPathOrErr = llvm::sys::findProgramByName(program); + if (!programPathOrErr) { + llvm::errs() << "Unable to find " << program << " in PATH\n"; + return false; + } + const std::string &programPath = *programPathOrErr; + + std::vector<const char *> args; + args.push_back(programPath.c_str()); + args.push_back(ctx.is64Bit() ? "/machine:x64" : "/machine:x86"); + args.push_back("/readonly"); + args.push_back("/nologo"); + args.push_back(outFileArg.c_str()); + for (const std::string &path : inFiles) + args.push_back(path.c_str()); + args.push_back(nullptr); + + if (llvm::sys::ExecuteAndWait(programPath.c_str(), &args[0]) != 0) { + llvm::errs() << program << " failed\n"; + return false; + } + result = outFile.str(); + return true; +} + +// Parse /manifestuac:(level=<string>|uiAccess=<string>). +// +// The arguments will be embedded to the manifest XML file with no error check, +// so the values given via the command line must be valid as XML attributes. +// This may sound a bit odd, but that's how link.exe works, so we will follow. +static bool parseManifestUAC(StringRef option, + llvm::Optional<std::string> &level, + llvm::Optional<std::string> &uiAccess) { + for (;;) { + option = option.ltrim(); + if (option.empty()) + return true; + if (option.startswith_lower("level=")) { + option = option.substr(strlen("level=")); + StringRef value; + std::tie(value, option) = option.split(" "); + level = value.str(); + continue; + } + if (option.startswith_lower("uiaccess=")) { + option = option.substr(strlen("uiaccess=")); + StringRef value; + std::tie(value, option) = option.split(" "); + uiAccess = value.str(); + continue; + } + return false; + } +} + +// Returns the machine type (e.g. x86) of the given input file. +// If the file is not COFF, returns false. +static bool getMachineType(StringRef path, llvm::COFF::MachineTypes &result) { + llvm::sys::fs::file_magic fileType; + if (llvm::sys::fs::identify_magic(path, fileType)) + return false; + if (fileType != llvm::sys::fs::file_magic::coff_object) + return false; + ErrorOr<std::unique_ptr<MemoryBuffer>> buf = MemoryBuffer::getFile(path); + if (!buf) + return false; + std::error_code ec; + llvm::object::COFFObjectFile obj(buf.get()->getMemBufferRef(), ec); + if (ec) + return false; + result = static_cast<llvm::COFF::MachineTypes>(obj.getMachine()); + return true; +} + +// Parse /export:entryname[=internalname][,@ordinal[,NONAME]][,DATA][,PRIVATE]. +// +// MSDN doesn't say anything about /export:foo=bar style option or PRIVATE +// attribtute, but link.exe actually accepts them. +static bool parseExport(StringRef option, + PECOFFLinkingContext::ExportDesc &ret) { + StringRef name; + StringRef rest; + std::tie(name, rest) = option.split(","); + if (name.empty()) + return false; + if (name.find('=') == StringRef::npos) { + ret.name = name; + } else { + std::tie(ret.externalName, ret.name) = name.split("="); + if (ret.name.empty()) + return false; + } + + for (;;) { + if (rest.empty()) + return true; + StringRef arg; + std::tie(arg, rest) = rest.split(","); + if (arg.equals_lower("noname")) { + if (ret.ordinal < 0) + return false; + ret.noname = true; + continue; + } + if (arg.equals_lower("data")) { + ret.isData = true; + continue; + } + if (arg.equals_lower("private")) { + ret.isPrivate = true; + continue; + } + if (arg.startswith("@")) { + int ordinal; + if (arg.substr(1).getAsInteger(0, ordinal)) + return false; + if (ordinal <= 0 || 65535 < ordinal) + return false; + ret.ordinal = ordinal; + continue; + } + return false; + } +} + +// Read module-definition file. +static bool parseDef(StringRef option, llvm::BumpPtrAllocator &alloc, + std::vector<moduledef::Directive *> &result) { + ErrorOr<std::unique_ptr<MemoryBuffer>> buf = MemoryBuffer::getFile(option); + if (!buf) + return false; + moduledef::Lexer lexer(std::move(buf.get())); + moduledef::Parser parser(lexer, alloc); + return parser.parse(result); +} + +static StringRef replaceExtension(PECOFFLinkingContext &ctx, StringRef path, + StringRef extension) { + SmallString<128> val = path; + llvm::sys::path::replace_extension(val, extension); + return ctx.allocate(val.str()); +} + +// Create a manifest file contents. +static std::string createManifestXml(PECOFFLinkingContext &ctx) { + std::string ret; + llvm::raw_string_ostream out(ret); + // Emit the XML. Note that we do *not* verify that the XML attributes are + // syntactically correct. This is intentional for link.exe compatibility. + out << "<?xml version=\"1.0\" standalone=\"yes\"?>\n" + "<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\"\n" + " manifestVersion=\"1.0\">\n"; + if (ctx.getManifestUAC()) { + out << " <trustInfo>\n" + " <security>\n" + " <requestedPrivileges>\n" + " <requestedExecutionLevel level=" << ctx.getManifestLevel() + << " uiAccess=" << ctx.getManifestUiAccess() + << "/>\n" + " </requestedPrivileges>\n" + " </security>\n" + " </trustInfo>\n"; + const std::string &dependency = ctx.getManifestDependency(); + if (!dependency.empty()) { + out << " <dependency>\n" + " <dependentAssembly>\n" + " <assemblyIdentity " << dependency + << " />\n" + " </dependentAssembly>\n" + " </dependency>\n"; + } + } + out << "</assembly>\n"; + out.flush(); + return ret; +} + +// Convert one doublequote to two doublequotes, so that we can embed the string +// into a resource script file. +static void quoteAndPrintXml(raw_ostream &out, StringRef str) { + for (;;) { + if (str.empty()) + return; + StringRef line; + std::tie(line, str) = str.split("\n"); + if (line.empty()) + continue; + out << '\"'; + const char *p = line.data(); + for (int i = 0, size = line.size(); i < size; ++i) { + switch (p[i]) { + case '\"': + out << '\"'; + // fallthrough + default: + out << p[i]; + } + } + out << "\"\n"; + } +} + +// Create a resource file (.res file) containing the manifest XML. This is done +// in two steps: +// +// 1. Create a resource script file containing the XML as a literal string. +// 2. Run RC.EXE command to compile the script file to a resource file. +// +// The temporary file created in step 1 will be deleted on exit from this +// function. The file created in step 2 will have the same lifetime as the +// PECOFFLinkingContext. +static bool createManifestResourceFile(PECOFFLinkingContext &ctx, + raw_ostream &diag, + std::string &resFile) { + // Create a temporary file for the resource script file. + SmallString<128> rcFileSmallString; + if (llvm::sys::fs::createTemporaryFile("tmp", "rc", rcFileSmallString)) { + diag << "Cannot create a temporary file\n"; + return false; + } + StringRef rcFile(rcFileSmallString.str()); + llvm::FileRemover rcFileRemover((Twine(rcFile))); + + // Open the temporary file for writing. + std::error_code ec; + llvm::raw_fd_ostream out(rcFileSmallString, ec, llvm::sys::fs::F_Text); + if (ec) { + diag << "Failed to open " << ctx.getManifestOutputPath() << ": " + << ec.message() << "\n"; + return false; + } + + // Write resource script to the RC file. + out << "#define LANG_ENGLISH 9\n" + << "#define SUBLANG_DEFAULT 1\n" + << "#define APP_MANIFEST " << ctx.getManifestId() << "\n" + << "#define RT_MANIFEST 24\n" + << "LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT\n" + << "APP_MANIFEST RT_MANIFEST {\n"; + quoteAndPrintXml(out, createManifestXml(ctx)); + out << "}\n"; + out.close(); + + // Create output resource file. + SmallString<128> resFileSmallString; + if (llvm::sys::fs::createTemporaryFile("tmp", "res", resFileSmallString)) { + diag << "Cannot create a temporary file"; + return false; + } + resFile = resFileSmallString.str(); + + // Register the resource file path so that the file will be deleted when the + // context's destructor is called. + ctx.registerTemporaryFile(resFile); + + // Run RC.EXE /fo tmp.res tmp.rc + std::string program = "rc.exe"; + ErrorOr<std::string> programPathOrErr = llvm::sys::findProgramByName(program); + if (!programPathOrErr) { + diag << "Unable to find " << program << " in PATH\n"; + return false; + } + const std::string &programPath = *programPathOrErr; + std::vector<const char *> args; + args.push_back(programPath.c_str()); + args.push_back("/fo"); + args.push_back(resFile.c_str()); + args.push_back("/nologo"); + args.push_back(rcFileSmallString.c_str()); + args.push_back(nullptr); + + if (llvm::sys::ExecuteAndWait(programPath.c_str(), &args[0]) != 0) { + diag << program << " failed\n"; + return false; + } + return true; +} + + +// Create the a side-by-side manifest file. +// +// The manifest file will convey some information to the linker, such as whether +// the binary needs to run as Administrator or not. Instead of being placed in +// the PE/COFF header, it's in XML format for some reason -- I guess it's +// probably because it's invented in the early dot-com era. +// +// The side-by-side manifest file is a separate XML file having ".manifest" +// extension. It will be created in the same directory as the resulting +// executable. +static bool createSideBySideManifestFile(PECOFFLinkingContext &ctx, + raw_ostream &diag) { + std::string path = ctx.getManifestOutputPath(); + if (path.empty()) { + // Default name of the manifest file is "foo.exe.manifest" where "foo.exe" is + // the output path. + path = ctx.outputPath(); + path.append(".manifest"); + } + + std::error_code ec; + llvm::raw_fd_ostream out(path, ec, llvm::sys::fs::F_Text); + if (ec) { + diag << ec.message() << "\n"; + return false; + } + out << createManifestXml(ctx); + return true; +} + +// Handle /failifmismatch option. +static bool +handleFailIfMismatchOption(StringRef option, + std::map<StringRef, StringRef> &mustMatch, + raw_ostream &diag) { + StringRef key, value; + std::tie(key, value) = option.split('='); + if (key.empty() || value.empty()) { + diag << "error: malformed /failifmismatch option: " << option << "\n"; + return true; + } + auto it = mustMatch.find(key); + if (it != mustMatch.end() && it->second != value) { + diag << "error: mismatch detected: '" << it->second << "' and '" << value + << "' for key '" << key << "'\n"; + return true; + } + mustMatch[key] = value; + return false; +} + +// +// Environment variable +// + +// Process "LINK" environment variable. If defined, the value of the variable +// should be processed as command line arguments. +static std::vector<const char *> processLinkEnv(PECOFFLinkingContext &ctx, + int argc, const char **argv) { + std::vector<const char *> ret; + // The first argument is the name of the command. This should stay at the head + // of the argument list. + assert(argc > 0); + ret.push_back(argv[0]); + + // Add arguments specified by the LINK environment variable. + llvm::Optional<std::string> env = llvm::sys::Process::GetEnv("LINK"); + if (env.hasValue()) + for (std::string &arg : splitArgList(*env)) + ret.push_back(ctx.allocate(arg).data()); + + // Add the rest of arguments passed via the command line. + for (int i = 1; i < argc; ++i) + ret.push_back(argv[i]); + ret.push_back(nullptr); + return ret; +} + +// Process "LIB" environment variable. The variable contains a list of search +// paths separated by semicolons. +static void processLibEnv(PECOFFLinkingContext &ctx) { + llvm::Optional<std::string> env = llvm::sys::Process::GetEnv("LIB"); + if (env.hasValue()) + for (StringRef path : splitPathList(*env)) + ctx.appendInputSearchPath(ctx.allocate(path)); +} + +namespace { +class DriverStringSaver : public llvm::cl::StringSaver { +public: + DriverStringSaver(PECOFFLinkingContext &ctx) : _ctx(ctx) {} + + const char *SaveString(const char *s) override { + return _ctx.allocate(StringRef(s)).data(); + } + +private: + PECOFFLinkingContext &_ctx; +}; +} + +// Tokenize command line options in a given file and add them to result. +static bool readResponseFile(StringRef path, PECOFFLinkingContext &ctx, + std::vector<const char *> &result) { + ArrayRef<uint8_t> contents; + if (!readFile(ctx, path, contents)) + return false; + StringRef contentsStr(reinterpret_cast<const char *>(contents.data()), + contents.size()); + DriverStringSaver saver(ctx); + SmallVector<const char *, 0> args; + llvm::cl::TokenizeWindowsCommandLine(contentsStr, saver, args); + for (const char *s : args) + result.push_back(s); + return true; +} + +// Expand arguments starting with "@". It's an error if a specified file does +// not exist. Returns true on success. +static bool expandResponseFiles(int &argc, const char **&argv, + PECOFFLinkingContext &ctx, raw_ostream &diag, + bool &expanded) { + std::vector<const char *> newArgv; + for (int i = 0; i < argc; ++i) { + if (argv[i][0] != '@') { + newArgv.push_back(argv[i]); + continue; + } + StringRef filename = StringRef(argv[i] + 1); + if (!readResponseFile(filename, ctx, newArgv)) { + diag << "error: cannot read response file: " << filename << "\n"; + return false; + } + expanded = true; + } + if (!expanded) + return true; + argc = newArgv.size(); + newArgv.push_back(nullptr); + argv = &ctx.allocateCopy(newArgv)[0]; + return true; +} + +// Parses the given command line options and returns the result. Returns NULL if +// there's an error in the options. +static std::unique_ptr<llvm::opt::InputArgList> +parseArgs(int argc, const char **argv, PECOFFLinkingContext &ctx, + raw_ostream &diag, bool isReadingDirectiveSection) { + // Expand arguments starting with "@". + bool expanded = false; + if (!expandResponseFiles(argc, argv, ctx, diag, expanded)) + return nullptr; + + // Parse command line options using WinLinkOptions.td + std::unique_ptr<llvm::opt::InputArgList> parsedArgs; + WinLinkOptTable table; + unsigned missingIndex; + unsigned missingCount; + parsedArgs.reset(table.ParseArgs(&argv[1], &argv[argc], + missingIndex, missingCount)); + if (missingCount) { + diag << "error: missing arg value for '" + << parsedArgs->getArgString(missingIndex) << "' expected " + << missingCount << " argument(s).\n"; + return nullptr; + } + + // Show warning for unknown arguments. In .drectve section, unknown options + // starting with "-?" are silently ignored. This is a COFF's feature to embed a + // new linker option to an object file while keeping backward compatibility. + for (auto unknownArg : parsedArgs->filtered(OPT_UNKNOWN)) { + StringRef arg = unknownArg->getSpelling(); + if (isReadingDirectiveSection && arg.startswith("-?")) + continue; + diag << "warning: ignoring unknown argument: " << arg << "\n"; + } + + // Copy mllvm + for (auto arg : parsedArgs->filtered(OPT_mllvm)) + ctx.appendLLVMOption(arg->getValue()); + + // If we have expaneded response files and /verbose is given, print out the + // final command line. + if (!isReadingDirectiveSection && expanded && + parsedArgs->getLastArg(OPT_verbose)) { + diag << "Command line:"; + for (int i = 0; i < argc; ++i) + diag << " " << argv[i]; + diag << "\n\n"; + } + + return parsedArgs; +} + +// Returns true if the given file node has already been added to the input +// graph. +static bool hasLibrary(PECOFFLinkingContext &ctx, File *file) { + StringRef path = file->path(); + for (std::unique_ptr<Node> &p : ctx.getNodes()) + if (auto *f = dyn_cast<FileNode>(p.get())) + if (f->getFile()->path() == path) + return true; + return false; +} + +// If the first command line argument is "/lib", link.exe acts as if it's +// "lib.exe" command. This is for backward compatibility. +// http://msdn.microsoft.com/en-us/library/h34w59b3.aspx +static bool maybeRunLibCommand(int argc, const char **argv, raw_ostream &diag) { + if (argc <= 1) + return false; + if (!StringRef(argv[1]).equals_lower("/lib")) + return false; + ErrorOr<std::string> pathOrErr = llvm::sys::findProgramByName("lib.exe"); + if (!pathOrErr) { + diag << "Unable to find lib.exe in PATH\n"; + return true; + } + const std::string &path = *pathOrErr; + + // Run lib.exe + std::vector<const char *> vec; + vec.push_back(path.c_str()); + for (int i = 2; i < argc; ++i) + vec.push_back(argv[i]); + vec.push_back(nullptr); + + if (llvm::sys::ExecuteAndWait(path.c_str(), &vec[0]) != 0) + diag << "lib.exe failed\n"; + return true; +} + +/// \brief Parse the input file to lld::File. +void addFiles(PECOFFLinkingContext &ctx, StringRef path, raw_ostream &diag, + std::vector<std::unique_ptr<File>> &files) { + for (std::unique_ptr<File> &file : loadFile(ctx, path, false)) { + if (ctx.logInputFiles()) + diag << file->path() << "\n"; + files.push_back(std::move(file)); + } +} + +// +// Main driver +// + +bool WinLinkDriver::linkPECOFF(int argc, const char **argv, raw_ostream &diag) { + if (maybeRunLibCommand(argc, argv, diag)) + return true; + + PECOFFLinkingContext ctx; + ctx.setParseDirectives(parseDirectives); + ctx.registry().addSupportCOFFObjects(ctx); + ctx.registry().addSupportCOFFImportLibraries(ctx); + ctx.registry().addSupportArchives(ctx.logInputFiles()); + ctx.registry().addSupportNativeObjects(); + ctx.registry().addSupportYamlFiles(); + + std::vector<const char *> newargv = processLinkEnv(ctx, argc, argv); + processLibEnv(ctx); + if (!parse(newargv.size() - 1, &newargv[0], ctx, diag)) + return false; + + // Create the file if needed. + if (ctx.getCreateManifest() && !ctx.getEmbedManifest()) + if (!createSideBySideManifestFile(ctx, diag)) + return false; + + return link(ctx, diag); +} + +bool WinLinkDriver::parse(int argc, const char *argv[], + PECOFFLinkingContext &ctx, raw_ostream &diag, + bool isReadingDirectiveSection) { + // Parse may be called from multiple threads simultaneously to parse .drectve + // sections. This function is not thread-safe because it mutates the context + // object. So acquire the lock. + std::lock_guard<std::recursive_mutex> lock(ctx.getMutex()); + + std::map<StringRef, StringRef> failIfMismatchMap; + // Parse the options. + std::unique_ptr<llvm::opt::InputArgList> parsedArgs = + parseArgs(argc, argv, ctx, diag, isReadingDirectiveSection); + if (!parsedArgs) + return false; + + // The list of input files. + std::vector<std::unique_ptr<File>> files; + std::vector<std::unique_ptr<File>> libraries; + + // Handle /help + if (parsedArgs->hasArg(OPT_help)) { + WinLinkOptTable table; + table.PrintHelp(llvm::outs(), argv[0], "LLVM Linker", false); + return false; + } + + // Handle /machine before parsing all the other options, as the target machine + // type affects how to handle other options. For example, x86 needs the + // leading underscore to mangle symbols, while x64 doesn't need it. + if (llvm::opt::Arg *inputArg = parsedArgs->getLastArg(OPT_machine)) { + StringRef arg = inputArg->getValue(); + llvm::COFF::MachineTypes type = stringToMachineType(arg); + if (type == llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN) { + diag << "error: unknown machine type: " << arg << "\n"; + return false; + } + ctx.setMachineType(type); + } else { + // If /machine option is missing, we need to take a look at + // the magic byte of the first object file to infer machine type. + std::vector<StringRef> filePaths; + for (auto arg : *parsedArgs) + if (arg->getOption().getID() == OPT_INPUT) + filePaths.push_back(arg->getValue()); + if (llvm::opt::Arg *arg = parsedArgs->getLastArg(OPT_DASH_DASH)) + filePaths.insert(filePaths.end(), arg->getValues().begin(), + arg->getValues().end()); + for (StringRef path : filePaths) { + llvm::COFF::MachineTypes type; + if (!getMachineType(path, type)) + continue; + if (type == llvm::COFF::IMAGE_FILE_MACHINE_UNKNOWN) + continue; + ctx.setMachineType(type); + break; + } + } + + // Handle /nodefaultlib:<lib>. The same option without argument is handled in + // the following for loop. + for (auto *arg : parsedArgs->filtered(OPT_nodefaultlib)) + ctx.addNoDefaultLib(arg->getValue()); + + // Handle /defaultlib. Argument of the option is added to the input file list + // unless it's blacklisted by /nodefaultlib. + std::vector<StringRef> defaultLibs; + for (auto *arg : parsedArgs->filtered(OPT_defaultlib)) + defaultLibs.push_back(arg->getValue()); + + // -alternatename:<alias>=<symbol> + for (auto *arg : parsedArgs->filtered(OPT_alternatename)) { + StringRef weak, def; + if (!parseAlternateName(arg->getValue(), weak, def, diag)) + return false; + ctx.addAlternateName(weak, def); + } + + // Parse /base command line option. The argument for the parameter is in + // the form of "<address>[:<size>]". + if (auto *arg = parsedArgs->getLastArg(OPT_base)) { + uint64_t addr, size; + // Size should be set to SizeOfImage field in the COFF header, and if + // it's smaller than the actual size, the linker should warn about that. + // Currently we just ignore the value of size parameter. + if (!parseMemoryOption(arg->getValue(), addr, size)) + return false; + ctx.setBaseAddress(addr); + } + + // Parse /dll command line option + if (parsedArgs->hasArg(OPT_dll)) { + ctx.setIsDll(true); + // Default base address of a DLL is 0x10000000. + if (!parsedArgs->hasArg(OPT_base)) + ctx.setBaseAddress(0x10000000); + } + + // Parse /stack command line option + if (auto *arg = parsedArgs->getLastArg(OPT_stack)) { + uint64_t reserve; + uint64_t commit = ctx.getStackCommit(); + if (!parseMemoryOption(arg->getValue(), reserve, commit)) + return false; + ctx.setStackReserve(reserve); + ctx.setStackCommit(commit); + } + + // Parse /heap command line option + if (auto *arg = parsedArgs->getLastArg(OPT_heap)) { + uint64_t reserve; + uint64_t commit = ctx.getHeapCommit(); + if (!parseMemoryOption(arg->getValue(), reserve, commit)) + return false; + ctx.setHeapReserve(reserve); + ctx.setHeapCommit(commit); + } + + if (auto *arg = parsedArgs->getLastArg(OPT_align)) { + uint32_t align; + StringRef val = arg->getValue(); + if (val.getAsInteger(10, align)) { + diag << "error: invalid value for /align: " << val << "\n"; + return false; + } + ctx.setSectionDefaultAlignment(align); + } + + if (auto *arg = parsedArgs->getLastArg(OPT_version)) { + uint32_t major, minor; + if (!parseVersion(arg->getValue(), major, minor)) + return false; + ctx.setImageVersion(PECOFFLinkingContext::Version(major, minor)); + } + + // Parse /merge:<from>=<to>. + for (auto *arg : parsedArgs->filtered(OPT_merge)) { + StringRef from, to; + std::tie(from, to) = StringRef(arg->getValue()).split('='); + if (from.empty() || to.empty()) { + diag << "error: malformed /merge option: " << arg->getValue() << "\n"; + return false; + } + if (!ctx.addSectionRenaming(diag, from, to)) + return false; + } + + // Parse /subsystem:<subsystem>[,<majorOSVersion>[.<minorOSVersion>]]. + if (auto *arg = parsedArgs->getLastArg(OPT_subsystem)) { + llvm::COFF::WindowsSubsystem subsystem; + llvm::Optional<uint32_t> major, minor; + if (!parseSubsystem(arg->getValue(), subsystem, major, minor, diag)) + return false; + ctx.setSubsystem(subsystem); + if (major.hasValue()) + ctx.setMinOSVersion(PECOFFLinkingContext::Version(*major, *minor)); + } + + // Parse /section:name,[[!]{DEKPRSW}] + for (auto *arg : parsedArgs->filtered(OPT_section)) { + std::string section; + llvm::Optional<uint32_t> flags, mask; + if (!parseSection(arg->getValue(), section, flags, mask)) { + diag << "Unknown argument for /section: " << arg->getValue() << "\n"; + return false; + } + if (flags.hasValue()) + ctx.setSectionSetMask(section, *flags); + if (mask.hasValue()) + ctx.setSectionClearMask(section, *mask); + } + + // Parse /manifest:EMBED[,ID=#]|NO. + if (auto *arg = parsedArgs->getLastArg(OPT_manifest_colon)) { + bool enable = true; + bool embed = false; + int id = 1; + if (!parseManifest(arg->getValue(), enable, embed, id)) { + diag << "Unknown argument for /manifest: " << arg->getValue() << "\n"; + return false; + } + ctx.setCreateManifest(enable); + ctx.setEmbedManifest(embed); + ctx.setManifestId(id); + } + + // Parse /manifestuac. + if (auto *arg = parsedArgs->getLastArg(OPT_manifestuac)) { + if (StringRef(arg->getValue()).equals_lower("no")) { + ctx.setManifestUAC(false); + } else { + llvm::Optional<std::string> privilegeLevel; + llvm::Optional<std::string> uiAccess; + if (!parseManifestUAC(arg->getValue(), privilegeLevel, uiAccess)) { + diag << "Unknown argument for /manifestuac: " << arg->getValue() + << "\n"; + return false; + } + if (privilegeLevel.hasValue()) + ctx.setManifestLevel(privilegeLevel.getValue()); + if (uiAccess.hasValue()) + ctx.setManifestUiAccess(uiAccess.getValue()); + } + } + + if (auto *arg = parsedArgs->getLastArg(OPT_manifestfile)) + ctx.setManifestOutputPath(ctx.allocate(arg->getValue())); + + // /manifestdependency:<string> option. Note that the argument will be + // embedded to the manifest XML file with no error check, for link.exe + // compatibility. We do not gurantete that the resulting XML file is + // valid. + if (auto *arg = parsedArgs->getLastArg(OPT_manifestdependency)) + ctx.setManifestDependency(ctx.allocate(arg->getValue())); + + for (auto *arg : parsedArgs->filtered(OPT_failifmismatch)) + if (handleFailIfMismatchOption(arg->getValue(), failIfMismatchMap, diag)) + return false; + + if (auto *arg = parsedArgs->getLastArg(OPT_entry)) + ctx.setEntrySymbolName(ctx.allocate(arg->getValue())); + + for (auto *arg : parsedArgs->filtered(OPT_export)) { + PECOFFLinkingContext::ExportDesc desc; + if (!parseExport(arg->getValue(), desc)) { + diag << "Error: malformed /export option: " << arg->getValue() << "\n"; + return false; + } + + // Mangle the symbol name only if it is reading user-supplied command line + // arguments. Because the symbol name in the .drectve section is already + // mangled by the compiler, we shouldn't add a leading underscore in that + // case. It's odd that the command line option has different semantics in + // the .drectve section, but this behavior is needed for compatibility + // with MSVC's link.exe. + if (!isReadingDirectiveSection) + desc.name = ctx.decorateSymbol(desc.name); + ctx.addDllExport(desc); + } + + for (auto *arg : parsedArgs->filtered(OPT_deffile)) { + llvm::BumpPtrAllocator alloc; + std::vector<moduledef::Directive *> dirs; + if (!parseDef(arg->getValue(), alloc, dirs)) { + diag << "Error: invalid module-definition file\n"; + return false; + } + for (moduledef::Directive *dir : dirs) { + if (auto *exp = dyn_cast<moduledef::Exports>(dir)) { + for (PECOFFLinkingContext::ExportDesc desc : exp->getExports()) { + desc.name = ctx.decorateSymbol(desc.name); + ctx.addDllExport(desc); + } + } else if (auto *hs = dyn_cast<moduledef::Heapsize>(dir)) { + ctx.setHeapReserve(hs->getReserve()); + ctx.setHeapCommit(hs->getCommit()); + } else if (auto *lib = dyn_cast<moduledef::Library>(dir)) { + ctx.setIsDll(true); + ctx.setOutputPath(ctx.allocate(lib->getName())); + if (lib->getBaseAddress() && !ctx.getBaseAddress()) + ctx.setBaseAddress(lib->getBaseAddress()); + } else if (auto *name = dyn_cast<moduledef::Name>(dir)) { + if (!name->getOutputPath().empty() && ctx.outputPath().empty()) + ctx.setOutputPath(ctx.allocate(name->getOutputPath())); + if (name->getBaseAddress() && ctx.getBaseAddress()) + ctx.setBaseAddress(name->getBaseAddress()); + } else if (auto *ver = dyn_cast<moduledef::Version>(dir)) { + ctx.setImageVersion(PECOFFLinkingContext::Version( + ver->getMajorVersion(), ver->getMinorVersion())); + } else { + llvm::dbgs() << static_cast<int>(dir->getKind()) << "\n"; + llvm_unreachable("Unknown module-definition directive.\n"); + } + } + } + + for (auto *arg : parsedArgs->filtered(OPT_libpath)) + ctx.appendInputSearchPath(ctx.allocate(arg->getValue())); + + for (auto *arg : parsedArgs->filtered(OPT_opt)) { + std::string val = StringRef(arg->getValue()).lower(); + if (val == "noref") { + ctx.setDeadStripping(false); + } else if (val != "ref" && val != "icf" && val != "noicf" && + val != "lbr" && val != "nolbr" && + !StringRef(val).startswith("icf=")) { + diag << "unknown option for /opt: " << val << "\n"; + return false; + } + } + + // LLD is not yet capable of creating a PDB file, so /debug does not have + // any effect. + // TODO: This should disable dead stripping. Currently we can't do that + // because removal of associative sections depends on dead stripping. + if (parsedArgs->hasArg(OPT_debug)) + ctx.setDebug(true); + + if (parsedArgs->hasArg(OPT_verbose)) + ctx.setLogInputFiles(true); + + // /force and /force:unresolved mean the same thing. We do not currently + // support /force:multiple. + if (parsedArgs->hasArg(OPT_force) || + parsedArgs->hasArg(OPT_force_unresolved)) { + ctx.setAllowRemainingUndefines(true); + } + + if (parsedArgs->hasArg(OPT_fixed)) { + // /fixed is not compatible with /dynamicbase. Check for it. + if (parsedArgs->hasArg(OPT_dynamicbase)) { + diag << "/dynamicbase must not be specified with /fixed\n"; + return false; + } + ctx.setBaseRelocationEnabled(false); + ctx.setDynamicBaseEnabled(false); + } + + // /swaprun:{cd,net} options set IMAGE_FILE_{REMOVABLE,NET}_RUN_FROM_SWAP + // bits in the COFF header, respectively. If one of the bits is on, the + // Windows loader will copy the entire file to swap area then execute it, + // so that the user can eject a CD or disconnect from the network. + if (parsedArgs->hasArg(OPT_swaprun_cd)) + ctx.setSwapRunFromCD(true); + + if (parsedArgs->hasArg(OPT_swaprun_net)) + ctx.setSwapRunFromNet(true); + + if (parsedArgs->hasArg(OPT_profile)) { + // /profile implies /opt:ref, /opt:noicf, /incremental:no and /fixed:no. + ctx.setDeadStripping(true); + ctx.setBaseRelocationEnabled(true); + ctx.setDynamicBaseEnabled(true); + } + + for (auto *arg : parsedArgs->filtered(OPT_implib)) + ctx.setOutputImportLibraryPath(arg->getValue()); + + for (auto *arg : parsedArgs->filtered(OPT_delayload)) { + ctx.addInitialUndefinedSymbol(ctx.getDelayLoadHelperName()); + ctx.addDelayLoadDLL(arg->getValue()); + } + + if (auto *arg = parsedArgs->getLastArg(OPT_stub)) { + ArrayRef<uint8_t> contents; + if (!readFile(ctx, arg->getValue(), contents)) { + diag << "Failed to read DOS stub file " << arg->getValue() << "\n"; + return false; + } + ctx.setDosStub(contents); + } + + for (auto *arg : parsedArgs->filtered(OPT_incl)) + ctx.addInitialUndefinedSymbol(ctx.allocate(arg->getValue())); + + if (parsedArgs->hasArg(OPT_noentry)) + ctx.setHasEntry(false); + + if (parsedArgs->hasArg(OPT_nodefaultlib_all)) + ctx.setNoDefaultLibAll(true); + + if (auto *arg = parsedArgs->getLastArg(OPT_out)) + ctx.setOutputPath(ctx.allocate(arg->getValue())); + + if (auto *arg = parsedArgs->getLastArg(OPT_pdb)) + ctx.setPDBFilePath(arg->getValue()); + + if (auto *arg = parsedArgs->getLastArg(OPT_lldmoduledeffile)) + ctx.setModuleDefinitionFile(arg->getValue()); + + std::vector<StringRef> inputFiles; + for (auto *arg : parsedArgs->filtered(OPT_INPUT)) + inputFiles.push_back(ctx.allocate(arg->getValue())); + +#define BOOLEAN_FLAG(name, setter) \ + if (auto *arg = parsedArgs->getLastArg(OPT_##name, OPT_##name##_no)) \ + ctx.setter(arg->getOption().matches(OPT_##name)); + + BOOLEAN_FLAG(nxcompat, setNxCompat); + BOOLEAN_FLAG(largeaddressaware, setLargeAddressAware); + BOOLEAN_FLAG(allowbind, setAllowBind); + BOOLEAN_FLAG(allowisolation, setAllowIsolation); + BOOLEAN_FLAG(dynamicbase, setDynamicBaseEnabled); + BOOLEAN_FLAG(tsaware, setTerminalServerAware); + BOOLEAN_FLAG(highentropyva, setHighEntropyVA); + BOOLEAN_FLAG(safeseh, setSafeSEH); +#undef BOOLEAN_FLAG + + // Arguments after "--" are interpreted as filenames even if they + // start with a hypen or a slash. This is not compatible with link.exe + // but useful for us to test lld on Unix. + if (llvm::opt::Arg *dashdash = parsedArgs->getLastArg(OPT_DASH_DASH)) + for (const StringRef value : dashdash->getValues()) + inputFiles.push_back(value); + + // Compile Windows resource files to compiled resource file. + if (ctx.getCreateManifest() && ctx.getEmbedManifest() && + !isReadingDirectiveSection) { + std::string resFile; + if (!createManifestResourceFile(ctx, diag, resFile)) + return false; + inputFiles.push_back(ctx.allocate(resFile)); + } + + // A Windows Resource file is not an object file. It contains data, + // such as an icon image, and is not in COFF file format. If resource + // files are given, the linker merge them into one COFF file using + // CVTRES.EXE and then link the resulting file. + { + auto it = std::partition(inputFiles.begin(), inputFiles.end(), + isResoruceFile); + if (it != inputFiles.begin()) { + std::vector<std::string> resFiles(inputFiles.begin(), it); + std::string resObj; + if (!convertResourceFiles(ctx, resFiles, resObj)) { + diag << "Failed to convert resource files\n"; + return false; + } + inputFiles = std::vector<StringRef>(it, inputFiles.end()); + inputFiles.push_back(ctx.allocate(resObj)); + ctx.registerTemporaryFile(resObj); + } + } + + // Prepare objects to add them to the list of input files. + for (StringRef path : inputFiles) { + path = ctx.allocate(path); + if (isLibraryFile(path)) { + addFiles(ctx, getLibraryPath(ctx, path), diag, libraries); + } else { + addFiles(ctx, getObjectPath(ctx, path), diag, files); + } + } + + // If dead-stripping is enabled, we need to add the entry symbol and + // symbols given by /include to the dead strip root set, so that it + // won't be removed from the output. + if (ctx.deadStrip()) + for (const StringRef symbolName : ctx.initialUndefinedSymbols()) + ctx.addDeadStripRoot(symbolName); + + // Add the libraries specified by /defaultlib unless they are already added + // nor blacklisted by /nodefaultlib. + if (!ctx.getNoDefaultLibAll()) + for (const StringRef path : defaultLibs) + if (!ctx.hasNoDefaultLib(path)) + addFiles(ctx, getLibraryPath(ctx, path.lower()), diag, libraries); + + if (files.empty() && !isReadingDirectiveSection) { + diag << "No input files\n"; + return false; + } + + // If /out option was not specified, the default output file name is + // constructed by replacing an extension of the first input file + // with ".exe". + if (ctx.outputPath().empty()) { + StringRef path = files[0]->path(); + ctx.setOutputPath(replaceExtension(ctx, path, ".exe")); + } + + // Add the input files to the linking context. + for (std::unique_ptr<File> &file : files) { + if (isReadingDirectiveSection) { + File *f = file.get(); + ctx.getTaskGroup().spawn([f] { f->parse(); }); + } + ctx.getNodes().push_back(llvm::make_unique<FileNode>(std::move(file))); + } + + // Add the library group to the linking context. + if (!isReadingDirectiveSection) { + // Add a group-end marker. + ctx.getNodes().push_back(llvm::make_unique<GroupEnd>(0)); + } + + // Add the library files to the library group. + for (std::unique_ptr<File> &file : libraries) { + if (!hasLibrary(ctx, file.get())) { + if (isReadingDirectiveSection) { + File *f = file.get(); + ctx.getTaskGroup().spawn([f] { f->parse(); }); + } + ctx.addLibraryFile(llvm::make_unique<FileNode>(std::move(file))); + } + } + + // Validate the combination of options used. + return ctx.validate(diag); +} + +} // namespace lld diff --git a/lib/Driver/WinLinkModuleDef.cpp b/lib/Driver/WinLinkModuleDef.cpp new file mode 100644 index 0000000000000..e55a0bc5fe649 --- /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 diff --git a/lib/Driver/WinLinkOptions.td b/lib/Driver/WinLinkOptions.td new file mode 100644 index 0000000000000..a545639b5bb27 --- /dev/null +++ b/lib/Driver/WinLinkOptions.td @@ -0,0 +1,120 @@ +include "llvm/Option/OptParser.td" + +// link.exe accepts options starting with either a dash or a slash. + +// Flag that takes no arguments. +class F<string name> : Flag<["/", "-", "-?"], name>; + +// Flag that takes one argument after ":". +class P<string name, string help> : + Joined<["/", "-", "-?"], name#":">, HelpText<help>; + +// Boolean flag suffixed by ":no". +multiclass B<string name, string help> { + def "" : F<name>; + def _no : F<name#":no">, HelpText<help>; +} + +def alternatename : P<"alternatename", "Define weak alias">; +def base : P<"base", "Base address of the program">; +def defaultlib : P<"defaultlib", "Add the library to the list of input files">; +def nodefaultlib : P<"nodefaultlib", "Remove a default library">; +def disallowlib : Joined<["/", "-", "-?"], "disallowlib:">, Alias<nodefaultlib>; +def entry : P<"entry", "Name of entry point symbol">; +// No help text because /failifmismatch is not intended to be used by the user. +def export : P<"export", "Export a function">; +def failifmismatch : P<"failifmismatch", "">; +def heap : P<"heap", "Size of the heap">; +def align : P<"align", "Section alignment">; +def libpath : P<"libpath", "Additional library search path">; +def mllvm : P<"mllvm", "Options to pass to LLVM">; +def out : P<"out", "Path to file to write output">; +def stack : P<"stack", "Size of the stack">; +def machine : P<"machine", "Specify target platform">; +def version : P<"version", "Specify a version number in the PE header">; +def merge : P<"merge", "Combine sections">; +def section : P<"section", "Specify section attributes">; +def subsystem : P<"subsystem", "Specify subsystem">; +def stub : P<"stub", "Specify DOS stub file">; +def opt : P<"opt", "Control optimizations">; +def implib : P<"implib", "Import library name">; +def delayload : P<"delayload", "Delay loaded DLL name">; +def pdb : P<"pdb", "PDB file path">; + +def manifest : F<"manifest">; +def manifest_colon : P<"manifest", "Create manifest file">; +def manifestuac : P<"manifestuac", "User access control">; +def manifestfile : P<"manifestfile", "Manifest file path">; +def manifestdependency : P<"manifestdependency", + "Attributes for <dependency> in manifest file">; + +// We cannot use multiclass P because class name "incl" is different +// from its command line option name. We do this because "include" is +// a reserved keyword in tablegen. +def incl : Joined<["/", "-"], "include:">, + HelpText<"Force symbol to be added to symbol table as undefined one">; + +// "def" is also a keyword. +def deffile : Joined<["/", "-"], "def:">, + HelpText<"Use module-definition file">; + +def nodefaultlib_all : F<"nodefaultlib">; +def noentry : F<"noentry">; +def dll : F<"dll">; +def verbose : F<"verbose">; +def debug : F<"debug">; +def swaprun_cd : F<"swaprun:cd">; +def swaprun_net : F<"swaprun:net">; +def profile : F<"profile">; + +def force : F<"force">, + HelpText<"Allow undefined symbols when creating executables">; +def force_unresolved : F<"force:unresolved">; + +defm nxcompat : B<"nxcompat", "Disable data execution provention">; +defm largeaddressaware : B<"largeaddressaware", "Disable large addresses">; +defm allowbind: B<"allowbind", "Disable DLL binding">; +defm fixed : B<"fixed", "Enable base relocations">; +defm tsaware : B<"tsaware", "Create non-Terminal Server aware executable">; +defm allowisolation : B<"allowisolation", "Set NO_ISOLATION bit">; +defm dynamicbase : B<"dynamicbase", + "Disable address space layout randomization">; +defm safeseh : B<"safeseh", "Produce an image with Safe Exception Handler">; +defm highentropyva : B<"highentropyva", "Set HIGH_ENTROPY_VA bit">; + +def help : F<"help">; +def help_q : Flag<["/?", "-?"], "">, Alias<help>; + +def DASH_DASH : Option<["--"], "", KIND_REMAINING_ARGS>; + +// Flag for debug +def lldmoduledeffile : Joined<["/", "-"], "lldmoduledeffile:">; + +//============================================================================== +// The flags below do nothing. They are defined only for link.exe compatibility. +//============================================================================== + +class QF<string name> : Joined<["/", "-", "-?"], name#":">; + +multiclass QB<string name> { + def "" : F<name>; + def _no : F<name#":no">; +} + +def functionpadmin : F<"functionpadmin">; +def ignoreidl : F<"ignoreidl">; +def incremental : F<"incremental">; +def no_incremental : F<"incremental:no">; +def nologo : F<"nologo">; + +def delay : QF<"delay">; +def errorreport : QF<"errorreport">; +def idlout : QF<"idlout">; +def ignore : QF<"ignore">; +def maxilksize : QF<"maxilksize">; +def pdbaltpath : QF<"pdbaltpath">; +def tlbid : QF<"tlbid">; +def tlbout : QF<"tlbout">; +def verbose_all : QF<"verbose">; + +defm wx : QB<"wx">; |