-
-
Save martell/0f510546ebb01a2fb04e to your computer and use it in GitHub Desktop.
COFF: gnu driver
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From f09e31b3e2a22dfddc6ae520300dd6cb9db64ab7 Mon Sep 17 00:00:00 2001 | |
From: Martell Malone <martellmalone@gmail.com> | |
Date: Sat, 15 Aug 2015 01:00:53 +0100 | |
Subject: [PATCH] COFF: add a gnu driver for mingw targets | |
diff --git a/COFF/CMakeLists.txt b/COFF/CMakeLists.txt | |
index 2bfd93b..dc1d3e3 100644 | |
--- a/COFF/CMakeLists.txt | |
+++ b/COFF/CMakeLists.txt | |
@@ -1,12 +1,19 @@ | |
set(LLVM_TARGET_DEFINITIONS Options.td) | |
tablegen(LLVM Options.inc -gen-opt-parser-defs) | |
-add_public_tablegen_target(COFFOptionsTableGen) | |
+add_public_tablegen_target(LINKOptionsTableGen) | |
+ | |
+# change to GNUOptions.td | |
+set(LLVM_TARGET_DEFINITIONS Options.td) | |
+tablegen(LLVM GNUOptions.inc -gen-opt-parser-defs) | |
+add_public_tablegen_target(GNUOptionsTableGen) | |
add_llvm_library(lldCOFF | |
Chunks.cpp | |
DLL.cpp | |
Driver.cpp | |
DriverUtils.cpp | |
+ GNUDriver.cpp | |
+ GNUDriverUtils.cpp | |
Error.cpp | |
ICF.cpp | |
InputFiles.cpp | |
@@ -28,4 +35,4 @@ add_llvm_library(lldCOFF | |
Support | |
) | |
-add_dependencies(lldCOFF COFFOptionsTableGen) | |
+add_dependencies(lldCOFF LINKOptionsTableGen GNUOptionsTableGen) | |
diff --git a/COFF/GNUDriver.cpp b/COFF/GNUDriver.cpp | |
new file mode 100644 | |
index 0000000..2cbe465 | |
--- /dev/null | |
+++ b/COFF/GNUDriver.cpp | |
@@ -0,0 +1,647 @@ | |
+//===- GNUDriver.cpp ---------------------------------------------------------===// | |
+// | |
+// The LLVM Linker | |
+// | |
+// This file is distributed under the University of Illinois Open Source | |
+// License. See LICENSE.TXT for details. | |
+// | |
+//===----------------------------------------------------------------------===// | |
+ | |
+#include "Config.h" | |
+#include "GNUDriver.h" | |
+#include "Error.h" | |
+#include "InputFiles.h" | |
+#include "SymbolTable.h" | |
+#include "Symbols.h" | |
+#include "Writer.h" | |
+#include "llvm/ADT/Optional.h" | |
+#include "llvm/ADT/STLExtras.h" | |
+#include "llvm/ADT/StringSwitch.h" | |
+#include "llvm/LibDriver/LibDriver.h" | |
+#include "llvm/Option/Arg.h" | |
+#include "llvm/Option/ArgList.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/TargetSelect.h" | |
+#include "llvm/Support/raw_ostream.h" | |
+#include <algorithm> | |
+#include <memory> | |
+ | |
+using namespace llvm; | |
+using namespace llvm::COFF; | |
+using llvm::sys::Process; | |
+using llvm::sys::fs::OpenFlags; | |
+using llvm::sys::fs::file_magic; | |
+using llvm::sys::fs::identify_magic; | |
+ | |
+namespace lld { | |
+namespace coff { | |
+ | |
+Configuration *Config; | |
+GNULinkerDriver *Driver; | |
+ | |
+void gnulink(llvm::ArrayRef<const char *> Args) { | |
+ auto C = make_unique<Configuration>(); | |
+ Config = C.get(); | |
+ auto D = make_unique<GNULinkerDriver>(); | |
+ Driver = D.get(); | |
+ return Driver->link(Args); | |
+} | |
+ | |
+// Drop directory components and replace extension with ".exe". | |
+static std::string getOutputPath(StringRef Path) { | |
+ auto P = Path.find_last_of("\\/"); | |
+ StringRef S = (P == StringRef::npos) ? Path : Path.substr(P + 1); | |
+ return (S.substr(0, S.rfind('.')) + ".exe").str(); | |
+} | |
+ | |
+// Opens a file. Path has to be resolved already. | |
+// Newly created memory buffers are owned by this driver. | |
+MemoryBufferRef GNULinkerDriver::openFile(StringRef Path) { | |
+ auto MBOrErr = MemoryBuffer::getFile(Path); | |
+ error(MBOrErr, Twine("Could not open ") + Path); | |
+ std::unique_ptr<MemoryBuffer> &MB = *MBOrErr; | |
+ MemoryBufferRef MBRef = MB->getMemBufferRef(); | |
+ OwningMBs.push_back(std::move(MB)); // take ownership | |
+ return MBRef; | |
+} | |
+ | |
+static std::unique_ptr<InputFile> createFile(MemoryBufferRef MB) { | |
+ // File type is detected by contents, not by file extension. | |
+ file_magic Magic = identify_magic(MB.getBuffer()); | |
+ if (Magic == file_magic::archive) | |
+ return std::unique_ptr<InputFile>(new ArchiveFile(MB)); | |
+ if (Magic == file_magic::bitcode) | |
+ return std::unique_ptr<InputFile>(new BitcodeFile(MB)); | |
+ if (Config->OutputFile == "") | |
+ Config->OutputFile = getOutputPath(MB.getBufferIdentifier()); | |
+ return std::unique_ptr<InputFile>(new ObjectFile(MB)); | |
+} | |
+ | |
+// Parses .drectve section contents and returns a list of files | |
+// specified by /defaultlib. | |
+void GNULinkerDriver::parseDirectives(StringRef S) { | |
+ llvm::opt::InputArgList Args = Parser.parse(S); | |
+ | |
+ for (auto *Arg : Args) { | |
+ switch (Arg->getOption().getID()) { | |
+ case OPT_alternatename: | |
+ parseAlternateName(Arg->getValue()); | |
+ break; | |
+ case OPT_defaultlib: | |
+ if (Optional<StringRef> Path = findLib(Arg->getValue())) { | |
+ MemoryBufferRef MB = openFile(*Path); | |
+ Symtab.addFile(createFile(MB)); | |
+ } | |
+ break; | |
+ case OPT_export: { | |
+ Export E = parseExport(Arg->getValue()); | |
+ if (Config->Machine == I386 && E.ExtName.startswith("_")) | |
+ E.ExtName = E.ExtName.substr(1); | |
+ Config->Exports.push_back(E); | |
+ break; | |
+ } | |
+ case OPT_failifmismatch: | |
+ checkFailIfMismatch(Arg->getValue()); | |
+ break; | |
+ case OPT_incl: | |
+ addUndefined(Arg->getValue()); | |
+ break; | |
+ case OPT_merge: | |
+ parseMerge(Arg->getValue()); | |
+ break; | |
+ case OPT_nodefaultlib: | |
+ Config->NoDefaultLibs.insert(doFindLib(Arg->getValue())); | |
+ break; | |
+ case OPT_editandcontinue: | |
+ case OPT_throwingnew: | |
+ break; | |
+ default: | |
+ error(Twine(Arg->getSpelling()) + " is not allowed in .drectve"); | |
+ } | |
+ } | |
+} | |
+ | |
+// Find file from search paths. You can omit ".o", this function takes | |
+// care of that. Note that the returned path is not guaranteed to exist. | |
+StringRef GNULinkerDriver::doFindFile(StringRef Filename) { | |
+ bool hasPathSep = (Filename.find_first_of("/\\") != StringRef::npos); | |
+ if (hasPathSep) | |
+ return Filename; | |
+ bool hasExt = (Filename.find('.') != StringRef::npos); | |
+ for (StringRef Dir : SearchPaths) { | |
+ SmallString<128> Path = Dir; | |
+ llvm::sys::path::append(Path, Filename); | |
+ if (llvm::sys::fs::exists(Path.str())) | |
+ return Alloc.save(Path.str()); | |
+ if (!hasExt) { | |
+ Path.append(".o"); | |
+ if (llvm::sys::fs::exists(Path.str())) | |
+ return Alloc.save(Path.str()); | |
+ } | |
+ } | |
+ return Filename; | |
+} | |
+ | |
+// Resolves a file path. This never returns the same path | |
+// (in that case, it returns None). | |
+Optional<StringRef> GNULinkerDriver::findFile(StringRef Filename) { | |
+ StringRef Path = doFindFile(Filename); | |
+ bool Seen = !VisitedFiles.insert(Path.lower()).second; | |
+ if (Seen) | |
+ return None; | |
+ return Path; | |
+} | |
+ | |
+// Find library file from search path. | |
+StringRef GNULinkerDriver::doFindLib(StringRef Filename) { | |
+ // Add ".lib" to Filename if that has no file extension. | |
+ bool hasExt = (Filename.find('.') != StringRef::npos); | |
+ if (!hasExt) | |
+ Filename = Alloc.save(Filename + ".lib"); | |
+ return doFindFile(Filename); | |
+} | |
+ | |
+// Resolves a library path. /nodefaultlib options are taken into | |
+// consideration. This never returns the same path (in that case, | |
+// it returns None). | |
+Optional<StringRef> GNULinkerDriver::findLib(StringRef Filename) { | |
+ if (Config->NoDefaultLibAll) | |
+ return None; | |
+ StringRef Path = doFindLib(Filename); | |
+ if (Config->NoDefaultLibs.count(Path)) | |
+ return None; | |
+ bool Seen = !VisitedFiles.insert(Path.lower()).second; | |
+ if (Seen) | |
+ return None; | |
+ return Path; | |
+} | |
+ | |
+// Parses LIB environment which contains a list of search paths. | |
+void GNULinkerDriver::addLibSearchPaths() { | |
+ Optional<std::string> EnvOpt = Process::GetEnv("LIB"); | |
+ if (!EnvOpt.hasValue()) | |
+ return; | |
+ StringRef Env = Alloc.save(*EnvOpt); | |
+ while (!Env.empty()) { | |
+ StringRef Path; | |
+ std::tie(Path, Env) = Env.split(';'); | |
+ SearchPaths.push_back(Path); | |
+ } | |
+} | |
+ | |
+Undefined *GNULinkerDriver::addUndefined(StringRef Name) { | |
+ Undefined *U = Symtab.addUndefined(Name); | |
+ Config->GCRoot.insert(U); | |
+ return U; | |
+} | |
+ | |
+// Symbol names are mangled by appending "_" prefix on x86. | |
+StringRef GNULinkerDriver::mangle(StringRef Sym) { | |
+ assert(Config->Machine != IMAGE_FILE_MACHINE_UNKNOWN); | |
+ if (Config->Machine == I386) | |
+ return Alloc.save("_" + Sym); | |
+ return Sym; | |
+} | |
+ | |
+// Windows specific -- find default entry point name. | |
+StringRef GNULinkerDriver::findDefaultEntry() { | |
+ // User-defined main functions and their corresponding entry points. | |
+ static const char *Entries[][2] = { | |
+ {"main", "mainCRTStartup"}, | |
+ {"wmain", "wmainCRTStartup"}, | |
+ {"WinMain", "WinMainCRTStartup"}, | |
+ {"wWinMain", "wWinMainCRTStartup"}, | |
+ }; | |
+ for (auto E : Entries) { | |
+ StringRef Entry = Symtab.findMangle(mangle(E[0])); | |
+ if (!Entry.empty() && !isa<Undefined>(Symtab.find(Entry)->Body)) | |
+ return mangle(E[1]); | |
+ } | |
+ return ""; | |
+} | |
+ | |
+WindowsSubsystem GNULinkerDriver::inferSubsystem() { | |
+ if (Config->DLL) | |
+ return IMAGE_SUBSYSTEM_WINDOWS_GUI; | |
+ if (Symtab.findUnderscore("main") || Symtab.findUnderscore("wmain")) | |
+ return IMAGE_SUBSYSTEM_WINDOWS_CUI; | |
+ if (Symtab.findUnderscore("WinMain") || Symtab.findUnderscore("wWinMain")) | |
+ return IMAGE_SUBSYSTEM_WINDOWS_GUI; | |
+ return IMAGE_SUBSYSTEM_UNKNOWN; | |
+} | |
+ | |
+static uint64_t getDefaultImageBase() { | |
+ if (Config->is64()) | |
+ return Config->DLL ? 0x180000000 : 0x140000000; | |
+ return Config->DLL ? 0x10000000 : 0x400000; | |
+} | |
+ | |
+void GNULinkerDriver::link(llvm::ArrayRef<const char *> ArgsArr) { | |
+ | |
+ // Needed for LTO. | |
+ llvm::InitializeAllTargetInfos(); | |
+ llvm::InitializeAllTargets(); | |
+ llvm::InitializeAllTargetMCs(); | |
+ llvm::InitializeAllAsmParsers(); | |
+ llvm::InitializeAllAsmPrinters(); | |
+ llvm::InitializeAllDisassemblers(); | |
+ | |
+ // Parse command line options. | |
+ llvm::opt::InputArgList Args = Parser.parse(ArgsArr.slice(1)); | |
+ | |
+ // Handle /help | |
+ if (Args.hasArg(OPT_help)) { | |
+ printHelp(ArgsArr[0]); | |
+ return; | |
+ } | |
+ | |
+ if (Args.filtered_begin(OPT_INPUT) == Args.filtered_end()) | |
+ error("no input files."); | |
+ | |
+ // Construct search path list. | |
+ SearchPaths.push_back(""); | |
+ for (auto *Arg : Args.filtered(OPT_libpath)) | |
+ SearchPaths.push_back(Arg->getValue()); | |
+ addLibSearchPaths(); | |
+ | |
+ // Handle /out | |
+ if (auto *Arg = Args.getLastArg(OPT_out)) | |
+ Config->OutputFile = Arg->getValue(); | |
+ | |
+ // Handle /verbose | |
+ if (Args.hasArg(OPT_verbose)) | |
+ Config->Verbose = true; | |
+ | |
+ // Handle /force or /force:unresolved | |
+ if (Args.hasArg(OPT_force) || Args.hasArg(OPT_force_unresolved)) | |
+ Config->Force = true; | |
+ | |
+ // Handle /debug | |
+ if (Args.hasArg(OPT_debug)) | |
+ Config->Debug = true; | |
+ | |
+ // Handle /noentry | |
+ if (Args.hasArg(OPT_noentry)) { | |
+ if (!Args.hasArg(OPT_dll)) | |
+ error("/noentry must be specified with /dll"); | |
+ Config->NoEntry = true; | |
+ } | |
+ | |
+ // Handle /dll | |
+ if (Args.hasArg(OPT_dll)) { | |
+ Config->DLL = true; | |
+ Config->ManifestID = 2; | |
+ } | |
+ | |
+ // Handle /fixed | |
+ if (Args.hasArg(OPT_fixed)) { | |
+ if (Args.hasArg(OPT_dynamicbase)) | |
+ error("/fixed must not be specified with /dynamicbase"); | |
+ Config->Relocatable = false; | |
+ Config->DynamicBase = false; | |
+ } | |
+ | |
+ // Handle /machine | |
+ if (auto *Arg = Args.getLastArg(OPT_machine)) | |
+ Config->Machine = getMachineType(Arg->getValue()); | |
+ | |
+ // Handle /nodefaultlib:<filename> | |
+ for (auto *Arg : Args.filtered(OPT_nodefaultlib)) | |
+ Config->NoDefaultLibs.insert(doFindLib(Arg->getValue())); | |
+ | |
+ // Handle /nodefaultlib | |
+ if (Args.hasArg(OPT_nodefaultlib_all)) | |
+ Config->NoDefaultLibAll = true; | |
+ | |
+ // Handle /base | |
+ if (auto *Arg = Args.getLastArg(OPT_base)) | |
+ parseNumbers(Arg->getValue(), &Config->ImageBase); | |
+ | |
+ // Handle /stack | |
+ if (auto *Arg = Args.getLastArg(OPT_stack)) | |
+ parseNumbers(Arg->getValue(), &Config->StackReserve, &Config->StackCommit); | |
+ | |
+ // Handle /heap | |
+ if (auto *Arg = Args.getLastArg(OPT_heap)) | |
+ parseNumbers(Arg->getValue(), &Config->HeapReserve, &Config->HeapCommit); | |
+ | |
+ // Handle /version | |
+ if (auto *Arg = Args.getLastArg(OPT_version)) | |
+ parseVersion(Arg->getValue(), &Config->MajorImageVersion, | |
+ &Config->MinorImageVersion); | |
+ | |
+ // Handle /subsystem | |
+ if (auto *Arg = Args.getLastArg(OPT_subsystem)) | |
+ parseSubsystem(Arg->getValue(), &Config->Subsystem, &Config->MajorOSVersion, | |
+ &Config->MinorOSVersion); | |
+ | |
+ // Handle /alternatename | |
+ for (auto *Arg : Args.filtered(OPT_alternatename)) | |
+ parseAlternateName(Arg->getValue()); | |
+ | |
+ // Handle /include | |
+ for (auto *Arg : Args.filtered(OPT_incl)) | |
+ addUndefined(Arg->getValue()); | |
+ | |
+ // Handle /implib | |
+ if (auto *Arg = Args.getLastArg(OPT_implib)) | |
+ Config->Implib = Arg->getValue(); | |
+ | |
+ // Handle /opt | |
+ for (auto *Arg : Args.filtered(OPT_opt)) { | |
+ std::string S = StringRef(Arg->getValue()).lower(); | |
+ if (S == "noref") { | |
+ Config->DoGC = false; | |
+ continue; | |
+ } | |
+ if (S == "lldicf") { | |
+ Config->ICF = true; | |
+ continue; | |
+ } | |
+ if (StringRef(S).startswith("lldlto=")) { | |
+ StringRef OptLevel = StringRef(S).substr(7); | |
+ if (OptLevel.getAsInteger(10, Config->LTOOptLevel) || | |
+ Config->LTOOptLevel > 3) { | |
+ error("/opt:lldlto: invalid optimization level: " + OptLevel); | |
+ } | |
+ continue; | |
+ } | |
+ if (S != "ref" && S != "icf" && S != "noicf" && | |
+ S != "lbr" && S != "nolbr" && | |
+ !StringRef(S).startswith("icf=")) { | |
+ error(Twine("/opt: unknown option: ") + S); | |
+ } | |
+ } | |
+ | |
+ // Handle /failifmismatch | |
+ for (auto *Arg : Args.filtered(OPT_failifmismatch)) | |
+ checkFailIfMismatch(Arg->getValue()); | |
+ | |
+ // Handle /merge | |
+ for (auto *Arg : Args.filtered(OPT_merge)) | |
+ parseMerge(Arg->getValue()); | |
+ | |
+ // Handle /manifest | |
+ if (auto *Arg = Args.getLastArg(OPT_manifest_colon)) | |
+ parseManifest(Arg->getValue()); | |
+ | |
+ // Handle /manifestuac | |
+ if (auto *Arg = Args.getLastArg(OPT_manifestuac)) | |
+ parseManifestUAC(Arg->getValue()); | |
+ | |
+ // Handle /manifestdependency | |
+ if (auto *Arg = Args.getLastArg(OPT_manifestdependency)) | |
+ Config->ManifestDependency = Arg->getValue(); | |
+ | |
+ // Handle /manifestfile | |
+ if (auto *Arg = Args.getLastArg(OPT_manifestfile)) | |
+ Config->ManifestFile = Arg->getValue(); | |
+ | |
+ // Handle miscellaneous boolean flags. | |
+ if (Args.hasArg(OPT_allowbind_no)) | |
+ Config->AllowBind = false; | |
+ if (Args.hasArg(OPT_allowisolation_no)) | |
+ Config->AllowIsolation = false; | |
+ if (Args.hasArg(OPT_dynamicbase_no)) | |
+ Config->DynamicBase = false; | |
+ if (Args.hasArg(OPT_nxcompat_no)) | |
+ Config->NxCompat = false; | |
+ if (Args.hasArg(OPT_tsaware_no)) | |
+ Config->TerminalServerAware = false; | |
+ | |
+ // Create a list of input files. Files can be given as arguments | |
+ // for /defaultlib option. | |
+ std::vector<StringRef> Paths; | |
+ std::vector<MemoryBufferRef> MBs; | |
+ for (auto *Arg : Args.filtered(OPT_INPUT)) | |
+ if (Optional<StringRef> Path = findFile(Arg->getValue())) | |
+ Paths.push_back(*Path); | |
+ for (auto *Arg : Args.filtered(OPT_defaultlib)) | |
+ if (Optional<StringRef> Path = findLib(Arg->getValue())) | |
+ Paths.push_back(*Path); | |
+ for (StringRef Path : Paths) | |
+ MBs.push_back(openFile(Path)); | |
+ | |
+ // Windows specific -- Create a resource file containing a manifest file. | |
+ if (Config->Manifest == Configuration::Embed) { | |
+ std::unique_ptr<MemoryBuffer> MB = createManifestRes(); | |
+ MBs.push_back(MB->getMemBufferRef()); | |
+ OwningMBs.push_back(std::move(MB)); // take ownership | |
+ } | |
+ | |
+ // Windows specific -- Input files can be Windows resource files (.res files). | |
+ // We invoke cvtres.exe to convert resource files to a regular COFF file | |
+ // then link the result file normally. | |
+ std::vector<MemoryBufferRef> Resources; | |
+ auto NotResource = [](MemoryBufferRef MB) { | |
+ return identify_magic(MB.getBuffer()) != file_magic::windows_resource; | |
+ }; | |
+ auto It = std::stable_partition(MBs.begin(), MBs.end(), NotResource); | |
+ if (It != MBs.end()) { | |
+ Resources.insert(Resources.end(), It, MBs.end()); | |
+ MBs.erase(It, MBs.end()); | |
+ } | |
+ | |
+ // Read all input files given via the command line. Note that step() | |
+ // doesn't read files that are specified by directive sections. | |
+ for (MemoryBufferRef MB : MBs) | |
+ Symtab.addFile(createFile(MB)); | |
+ Symtab.step(); | |
+ | |
+ // Determine machine type and check if all object files are | |
+ // for the same CPU type. Note that this needs to be done before | |
+ // any call to mangle(). | |
+ for (std::unique_ptr<InputFile> &File : Symtab.getFiles()) { | |
+ MachineTypes MT = File->getMachineType(); | |
+ if (MT == IMAGE_FILE_MACHINE_UNKNOWN) | |
+ continue; | |
+ if (Config->Machine == IMAGE_FILE_MACHINE_UNKNOWN) { | |
+ Config->Machine = MT; | |
+ continue; | |
+ } | |
+ if (Config->Machine != MT) | |
+ error(Twine(File->getShortName()) + ": machine type " + machineToStr(MT) + | |
+ " conflicts with " + machineToStr(Config->Machine)); | |
+ } | |
+ if (Config->Machine == IMAGE_FILE_MACHINE_UNKNOWN) { | |
+ llvm::errs() << "warning: /machine is not specified. x64 is assumed.\n"; | |
+ Config->Machine = AMD64; | |
+ } | |
+ | |
+ // Windows specific -- Convert Windows resource files to a COFF file. | |
+ if (!Resources.empty()) { | |
+ std::unique_ptr<MemoryBuffer> MB = convertResToCOFF(Resources); | |
+ Symtab.addFile(createFile(MB->getMemBufferRef())); | |
+ OwningMBs.push_back(std::move(MB)); // take ownership | |
+ } | |
+ | |
+ // Handle /largeaddressaware | |
+ if (Config->is64() || Args.hasArg(OPT_largeaddressaware)) | |
+ Config->LargeAddressAware = true; | |
+ | |
+ // Handle /highentropyva | |
+ if (Config->is64() && !Args.hasArg(OPT_highentropyva_no)) | |
+ Config->HighEntropyVA = true; | |
+ | |
+ // Handle /entry and /dll | |
+ if (auto *Arg = Args.getLastArg(OPT_entry)) { | |
+ Config->Entry = addUndefined(mangle(Arg->getValue())); | |
+ } else if (Args.hasArg(OPT_dll) && !Config->NoEntry) { | |
+ StringRef S = (Config->Machine == I386) ? "__DllMainCRTStartup@12" | |
+ : "_DllMainCRTStartup"; | |
+ Config->Entry = addUndefined(S); | |
+ } else if (!Config->NoEntry) { | |
+ // Windows specific -- If entry point name is not given, we need to | |
+ // infer that from user-defined entry name. | |
+ StringRef S = findDefaultEntry(); | |
+ if (S.empty()) | |
+ error("entry point must be defined"); | |
+ Config->Entry = addUndefined(S); | |
+ if (Config->Verbose) | |
+ llvm::outs() << "Entry name inferred: " << S << "\n"; | |
+ } | |
+ | |
+ // Handle /export | |
+ for (auto *Arg : Args.filtered(OPT_export)) { | |
+ Export E = parseExport(Arg->getValue()); | |
+ if (Config->Machine == I386 && !E.Name.startswith("_@?")) | |
+ E.Name = mangle(E.Name); | |
+ Config->Exports.push_back(E); | |
+ } | |
+ | |
+ // Handle /def | |
+ if (auto *Arg = Args.getLastArg(OPT_deffile)) { | |
+ MemoryBufferRef MB = openFile(Arg->getValue()); | |
+ // parseModuleDefs mutates Config object. | |
+ parseModuleDefs(MB, &Alloc); | |
+ } | |
+ | |
+ // Handle /delayload | |
+ for (auto *Arg : Args.filtered(OPT_delayload)) { | |
+ Config->DelayLoads.insert(StringRef(Arg->getValue()).lower()); | |
+ if (Config->Machine == I386) { | |
+ Config->DelayLoadHelper = addUndefined("___delayLoadHelper2@8"); | |
+ } else { | |
+ Config->DelayLoadHelper = addUndefined("__delayLoadHelper2"); | |
+ } | |
+ } | |
+ | |
+ // Set default image base if /base is not given. | |
+ if (Config->ImageBase == uint64_t(-1)) | |
+ Config->ImageBase = getDefaultImageBase(); | |
+ | |
+ Symtab.addRelative(mangle("__image_base__"), 0); | |
+ if (Config->Machine == I386) { | |
+ Config->SEHTable = Symtab.addRelative("___safe_se_handler_table", 0); | |
+ Config->SEHCount = Symtab.addAbsolute("___safe_se_handler_count", 0); | |
+ } | |
+ | |
+ // We do not support /guard:cf (control flow protection) yet. | |
+ // Define CFG symbols anyway so that we can link MSVC 2015 CRT. | |
+ Symtab.addAbsolute(mangle("__guard_fids_table"), 0); | |
+ Symtab.addAbsolute(mangle("__guard_fids_count"), 0); | |
+ Symtab.addAbsolute(mangle("__guard_flags"), 0x100); | |
+ | |
+ // Read as much files as we can from directives sections. | |
+ Symtab.run(); | |
+ | |
+ // Resolve auxiliary symbols until we get a convergence. | |
+ // (Trying to resolve a symbol may trigger a Lazy symbol to load a new file. | |
+ // A new file may contain a directive section to add new command line options. | |
+ // That's why we have to repeat until converge.) | |
+ for (;;) { | |
+ // Windows specific -- if entry point is not found, | |
+ // search for its mangled names. | |
+ if (Config->Entry) | |
+ Symtab.mangleMaybe(Config->Entry); | |
+ | |
+ // Windows specific -- Make sure we resolve all dllexported symbols. | |
+ for (Export &E : Config->Exports) { | |
+ E.Sym = addUndefined(E.Name); | |
+ Symtab.mangleMaybe(E.Sym); | |
+ } | |
+ | |
+ // Add weak aliases. Weak aliases is a mechanism to give remaining | |
+ // undefined symbols final chance to be resolved successfully. | |
+ for (auto Pair : Config->AlternateNames) { | |
+ StringRef From = Pair.first; | |
+ StringRef To = Pair.second; | |
+ Symbol *Sym = Symtab.find(From); | |
+ if (!Sym) | |
+ continue; | |
+ if (auto *U = dyn_cast<Undefined>(Sym->Body)) | |
+ if (!U->WeakAlias) | |
+ U->WeakAlias = Symtab.addUndefined(To); | |
+ } | |
+ | |
+ // Windows specific -- if __load_config_used can be resolved, resolve it. | |
+ if (Symtab.findUnderscore("_load_config_used")) | |
+ addUndefined(mangle("_load_config_used")); | |
+ | |
+ if (Symtab.queueEmpty()) | |
+ break; | |
+ Symtab.run(); | |
+ } | |
+ | |
+ // Do LTO by compiling bitcode input files to a native COFF file | |
+ // then link that file. | |
+ Symtab.addCombinedLTOObject(); | |
+ | |
+ // Make sure we have resolved all symbols. | |
+ Symtab.reportRemainingUndefines(/*Resolve=*/true); | |
+ | |
+ // Windows specific -- if no /subsystem is given, we need to infer | |
+ // that from entry point name. | |
+ if (Config->Subsystem == IMAGE_SUBSYSTEM_UNKNOWN) { | |
+ Config->Subsystem = inferSubsystem(); | |
+ if (Config->Subsystem == IMAGE_SUBSYSTEM_UNKNOWN) | |
+ error("subsystem must be defined"); | |
+ } | |
+ | |
+ // Handle /safeseh. | |
+ if (Args.hasArg(OPT_safeseh)) { | |
+ for (ObjectFile *File : Symtab.ObjectFiles) { | |
+ if (File->SEHCompat) | |
+ continue; | |
+ error(Twine("/safeseh: ") + File->getName() + | |
+ " is not compatible with SEH"); | |
+ } | |
+ } | |
+ | |
+ // Windows specific -- when we are creating a .dll file, we also | |
+ // need to create a .lib file. | |
+ if (!Config->Exports.empty()) { | |
+ fixupExports(); | |
+ writeImportLibrary(); | |
+ assignExportOrdinals(); | |
+ } | |
+ | |
+ // Windows specific -- Create a side-by-side manifest file. | |
+ if (Config->Manifest == Configuration::SideBySide) | |
+ createSideBySideManifest(); | |
+ | |
+ // Create a dummy PDB file to satisfy build sytem rules. | |
+ if (auto *Arg = Args.getLastArg(OPT_pdb)) | |
+ touchFile(Arg->getValue()); | |
+ | |
+ // Write the result. | |
+ writeResult(&Symtab); | |
+ | |
+ // Create a symbol map file containing symbol VAs and their names | |
+ // to help debugging. | |
+ if (auto *Arg = Args.getLastArg(OPT_lldmap)) { | |
+ std::error_code EC; | |
+ llvm::raw_fd_ostream Out(Arg->getValue(), EC, OpenFlags::F_Text); | |
+ error(EC, "Could not create the symbol map"); | |
+ Symtab.printMap(Out); | |
+ } | |
+ // Call exit to avoid calling destructors. | |
+ exit(0); | |
+} | |
+ | |
+} // namespace coff | |
+} // namespace lld | |
diff --git a/COFF/GNUDriver.h b/COFF/GNUDriver.h | |
new file mode 100644 | |
index 0000000..1d0115b | |
--- /dev/null | |
+++ b/COFF/GNUDriver.h | |
@@ -0,0 +1,174 @@ | |
+//===- GNUDriver.h ----------------------------------------------*- C++ -*-===// | |
+// | |
+// The LLVM Linker | |
+// | |
+// This file is distributed under the University of Illinois Open Source | |
+// License. See LICENSE.TXT for details. | |
+// | |
+//===----------------------------------------------------------------------===// | |
+ | |
+#ifndef LLD_COFF_DRIVER_H | |
+#define LLD_COFF_DRIVER_H | |
+ | |
+#include "Config.h" | |
+#include "SymbolTable.h" | |
+#include "lld/Core/LLVM.h" | |
+#include "llvm/ADT/Optional.h" | |
+#include "llvm/ADT/StringRef.h" | |
+#include "llvm/Object/COFF.h" | |
+#include "llvm/Option/Arg.h" | |
+#include "llvm/Option/ArgList.h" | |
+#include "llvm/Support/StringSaver.h" | |
+#include <memory> | |
+#include <set> | |
+#include <system_error> | |
+#include <vector> | |
+ | |
+namespace lld { | |
+namespace coff { | |
+ | |
+class GNULinkerDriver; | |
+extern GNULinkerDriver *Driver; | |
+ | |
+using llvm::COFF::MachineTypes; | |
+using llvm::COFF::WindowsSubsystem; | |
+using llvm::Optional; | |
+class InputFile; | |
+ | |
+// Entry point of the COFF linker. | |
+void gnulink(llvm::ArrayRef<const char *> Args); | |
+ | |
+class ArgParser { | |
+public: | |
+ ArgParser() : Alloc(AllocAux) {} | |
+ // Parses command line options. | |
+ llvm::opt::InputArgList parse(llvm::ArrayRef<const char *> Args); | |
+ | |
+ // Concatenate LINK environment varirable and given arguments and parse them. | |
+ llvm::opt::InputArgList parseLD(llvm::ArrayRef<const char *> Args); | |
+ | |
+ // Tokenizes a given string and then parses as command line options. | |
+ llvm::opt::InputArgList parse(StringRef S) { return parse(tokenize(S)); } | |
+ | |
+private: | |
+ std::vector<const char *> tokenize(StringRef S); | |
+ | |
+ std::vector<const char *> replaceResponseFiles(std::vector<const char *>); | |
+ | |
+ llvm::BumpPtrAllocator AllocAux; | |
+ llvm::StringSaver Alloc; | |
+}; | |
+ | |
+class GNULinkerDriver { | |
+public: | |
+ GNULinkerDriver() : Alloc(AllocAux) {} | |
+ void link(llvm::ArrayRef<const char *> Args); | |
+ | |
+ // Used by the resolver to parse .drectve section contents. | |
+ void parseDirectives(StringRef S); | |
+ | |
+private: | |
+ llvm::BumpPtrAllocator AllocAux; | |
+ llvm::StringSaver Alloc; | |
+ ArgParser Parser; | |
+ SymbolTable Symtab; | |
+ | |
+ // Opens a file. Path has to be resolved already. | |
+ MemoryBufferRef openFile(StringRef Path); | |
+ | |
+ // Searches a file from search paths. | |
+ Optional<StringRef> findFile(StringRef Filename); | |
+ Optional<StringRef> findLib(StringRef Filename); | |
+ StringRef doFindFile(StringRef Filename); | |
+ StringRef doFindLib(StringRef Filename); | |
+ | |
+ // Parses LIB environment which contains a list of search paths. | |
+ void addLibSearchPaths(); | |
+ | |
+ // Library search path. The first element is always "" (current directory). | |
+ std::vector<StringRef> SearchPaths; | |
+ std::set<std::string> VisitedFiles; | |
+ | |
+ Undefined *addUndefined(StringRef Sym); | |
+ StringRef mangle(StringRef Sym); | |
+ | |
+ // Windows specific -- "main" is not the only main function in Windows. | |
+ // You can choose one from these four -- {w,}{WinMain,main}. | |
+ // There are four different entry point functions for them, | |
+ // {w,}{WinMain,main}CRTStartup, respectively. The linker needs to | |
+ // choose the right one depending on which "main" function is defined. | |
+ // This function looks up the symbol table and resolve corresponding | |
+ // entry point name. | |
+ StringRef findDefaultEntry(); | |
+ WindowsSubsystem inferSubsystem(); | |
+ | |
+ // Driver is the owner of all opened files. | |
+ // InputFiles have MemoryBufferRefs to them. | |
+ std::vector<std::unique_ptr<MemoryBuffer>> OwningMBs; | |
+}; | |
+ | |
+void parseModuleDefs(MemoryBufferRef MB, llvm::StringSaver *Alloc); | |
+void writeImportLibrary(); | |
+ | |
+// Functions below this line are defined in DriverUtils.cpp. | |
+ | |
+void printHelp(const char *Argv0); | |
+ | |
+// For /machine option. | |
+MachineTypes getMachineType(StringRef Arg); | |
+StringRef machineToStr(MachineTypes MT); | |
+ | |
+// Parses a string in the form of "<integer>[,<integer>]". | |
+void parseNumbers(StringRef Arg, uint64_t *Addr, uint64_t *Size = nullptr); | |
+ | |
+// Parses a string in the form of "<integer>[.<integer>]". | |
+// Minor's default value is 0. | |
+void parseVersion(StringRef Arg, uint32_t *Major, uint32_t *Minor); | |
+ | |
+// Parses a string in the form of "<subsystem>[,<integer>[.<integer>]]". | |
+void parseSubsystem(StringRef Arg, WindowsSubsystem *Sys, uint32_t *Major, | |
+ uint32_t *Minor); | |
+ | |
+void parseAlternateName(StringRef); | |
+void parseMerge(StringRef); | |
+ | |
+// Parses a string in the form of "EMBED[,=<integer>]|NO". | |
+void parseManifest(StringRef Arg); | |
+ | |
+// Parses a string in the form of "level=<string>|uiAccess=<string>" | |
+void parseManifestUAC(StringRef Arg); | |
+ | |
+// Create a resource file containing a manifest XML. | |
+std::unique_ptr<MemoryBuffer> createManifestRes(); | |
+void createSideBySideManifest(); | |
+ | |
+// Used for dllexported symbols. | |
+Export parseExport(StringRef Arg); | |
+void fixupExports(); | |
+void assignExportOrdinals(); | |
+ | |
+// Parses a string in the form of "key=value" and check | |
+// if value matches previous values for the key. | |
+// This feature used in the directive section to reject | |
+// incompatible objects. | |
+void checkFailIfMismatch(StringRef Arg); | |
+ | |
+// Convert Windows resource files (.res files) to a .obj file | |
+// using cvtres.exe. | |
+std::unique_ptr<MemoryBuffer> | |
+convertResToCOFF(const std::vector<MemoryBufferRef> &MBs); | |
+ | |
+void touchFile(StringRef Path); | |
+ | |
+// Create enum with OPT_xxx values for each option in Options.td | |
+enum { | |
+ OPT_INVALID = 0, | |
+#define OPTION(_1, _2, ID, _4, _5, _6, _7, _8, _9, _10, _11) OPT_##ID, | |
+#include "GNUOptions.inc" | |
+#undef OPTION | |
+}; | |
+ | |
+} // namespace coff | |
+} // namespace lld | |
+ | |
+#endif | |
diff --git a/COFF/GNUDriverUtils.cpp b/COFF/GNUDriverUtils.cpp | |
new file mode 100644 | |
index 0000000..8b2233c | |
--- /dev/null | |
+++ b/COFF/GNUDriverUtils.cpp | |
@@ -0,0 +1,503 @@ | |
+//===- GNUDriverUtils.cpp -------------------------------------------------===// | |
+// | |
+// The LLVM Linker | |
+// | |
+// This file is distributed under the University of Illinois Open Source | |
+// License. See LICENSE.TXT for details. | |
+// | |
+//===----------------------------------------------------------------------===// | |
+// | |
+// This file contains utility functions for the gnu driver. | |
+// We created this because there are so many small functions. | |
+// This seperate file to makes Driver.cpp less cluttered. | |
+// | |
+//===----------------------------------------------------------------------===// | |
+ | |
+#include "Config.h" | |
+#include "GNUDriver.h" | |
+#include "Error.h" | |
+#include "Symbols.h" | |
+#include "llvm/ADT/Optional.h" | |
+#include "llvm/ADT/STLExtras.h" | |
+#include "llvm/ADT/StringSwitch.h" | |
+#include "llvm/Object/COFF.h" | |
+#include "llvm/Option/Arg.h" | |
+#include "llvm/Option/ArgList.h" | |
+#include "llvm/Option/Option.h" | |
+#include "llvm/Support/CommandLine.h" | |
+#include "llvm/Support/FileUtilities.h" | |
+#include "llvm/Support/Path.h" | |
+#include "llvm/Support/Process.h" | |
+#include "llvm/Support/Program.h" | |
+#include "llvm/Support/raw_ostream.h" | |
+#include <memory> | |
+ | |
+using namespace llvm::COFF; | |
+using namespace llvm; | |
+using llvm::cl::ExpandResponseFiles; | |
+using llvm::cl::TokenizeWindowsCommandLine; | |
+using llvm::sys::Process; | |
+ | |
+namespace lld { | |
+namespace coff { | |
+ | |
+// Returns /machine's value. | |
+MachineTypes getMachineType(StringRef S) { | |
+ MachineTypes MT = StringSwitch<MachineTypes>(S.lower()) | |
+ .Case("x64", AMD64) | |
+ .Case("amd64", AMD64) | |
+ .Case("x86", I386) | |
+ .Case("i386", I386) | |
+ .Case("arm", ARMNT) | |
+ .Default(IMAGE_FILE_MACHINE_UNKNOWN); | |
+ if (MT != IMAGE_FILE_MACHINE_UNKNOWN) | |
+ return MT; | |
+ error(Twine("unknown /machine argument: ") + S); | |
+} | |
+ | |
+StringRef machineToStr(MachineTypes MT) { | |
+ switch (MT) { | |
+ case ARMNT: | |
+ return "arm"; | |
+ case AMD64: | |
+ return "x64"; | |
+ case I386: | |
+ return "x86"; | |
+ default: | |
+ llvm_unreachable("unknown machine type"); | |
+ } | |
+} | |
+ | |
+// Parses a string in the form of "<integer>[,<integer>]". | |
+void parseNumbers(StringRef Arg, uint64_t *Addr, uint64_t *Size) { | |
+ StringRef S1, S2; | |
+ std::tie(S1, S2) = Arg.split(','); | |
+ if (S1.getAsInteger(0, *Addr)) | |
+ error(Twine("invalid number: ") + S1); | |
+ if (Size && !S2.empty() && S2.getAsInteger(0, *Size)) | |
+ error(Twine("invalid number: ") + S2); | |
+} | |
+ | |
+// Parses a string in the form of "<integer>[.<integer>]". | |
+// If second number is not present, Minor is set to 0. | |
+void parseVersion(StringRef Arg, uint32_t *Major, uint32_t *Minor) { | |
+ StringRef S1, S2; | |
+ std::tie(S1, S2) = Arg.split('.'); | |
+ if (S1.getAsInteger(0, *Major)) | |
+ error(Twine("invalid number: ") + S1); | |
+ *Minor = 0; | |
+ if (!S2.empty() && S2.getAsInteger(0, *Minor)) | |
+ error(Twine("invalid number: ") + S2); | |
+} | |
+ | |
+// Parses a string in the form of "<subsystem>[,<integer>[.<integer>]]". | |
+void parseSubsystem(StringRef Arg, WindowsSubsystem *Sys, uint32_t *Major, | |
+ uint32_t *Minor) { | |
+ StringRef SysStr, Ver; | |
+ std::tie(SysStr, Ver) = Arg.split(','); | |
+ *Sys = StringSwitch<WindowsSubsystem>(SysStr.lower()) | |
+ .Case("boot_application", IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION) | |
+ .Case("console", IMAGE_SUBSYSTEM_WINDOWS_CUI) | |
+ .Case("efi_application", IMAGE_SUBSYSTEM_EFI_APPLICATION) | |
+ .Case("efi_boot_service_driver", IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER) | |
+ .Case("efi_rom", IMAGE_SUBSYSTEM_EFI_ROM) | |
+ .Case("efi_runtime_driver", IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER) | |
+ .Case("native", IMAGE_SUBSYSTEM_NATIVE) | |
+ .Case("posix", IMAGE_SUBSYSTEM_POSIX_CUI) | |
+ .Case("windows", IMAGE_SUBSYSTEM_WINDOWS_GUI) | |
+ .Default(IMAGE_SUBSYSTEM_UNKNOWN); | |
+ if (*Sys == IMAGE_SUBSYSTEM_UNKNOWN) | |
+ error(Twine("unknown subsystem: ") + SysStr); | |
+ if (!Ver.empty()) | |
+ parseVersion(Ver, Major, Minor); | |
+} | |
+ | |
+// Parse a string of the form of "<from>=<to>". | |
+// Results are directly written to Config. | |
+void parseAlternateName(StringRef S) { | |
+ StringRef From, To; | |
+ std::tie(From, To) = S.split('='); | |
+ if (From.empty() || To.empty()) | |
+ error(Twine("/alternatename: invalid argument: ") + S); | |
+ auto It = Config->AlternateNames.find(From); | |
+ if (It != Config->AlternateNames.end() && It->second != To) | |
+ error(Twine("/alternatename: conflicts: ") + S); | |
+ Config->AlternateNames.insert(It, std::make_pair(From, To)); | |
+} | |
+ | |
+// Parse a string of the form of "<from>=<to>". | |
+// Results are directly written to Config. | |
+void parseMerge(StringRef S) { | |
+ StringRef From, To; | |
+ std::tie(From, To) = S.split('='); | |
+ if (From.empty() || To.empty()) | |
+ error(Twine("/merge: invalid argument: ") + S); | |
+ auto Pair = Config->Merge.insert(std::make_pair(From, To)); | |
+ bool Inserted = Pair.second; | |
+ if (!Inserted) { | |
+ StringRef Existing = Pair.first->second; | |
+ if (Existing != To) | |
+ llvm::errs() << "warning: " << S << ": already merged into " | |
+ << Existing << "\n"; | |
+ } | |
+} | |
+ | |
+// Parses a string in the form of "EMBED[,=<integer>]|NO". | |
+// Results are directly written to Config. | |
+void parseManifest(StringRef Arg) { | |
+ if (Arg.equals_lower("no")) { | |
+ Config->Manifest = Configuration::No; | |
+ return; | |
+ } | |
+ if (!Arg.startswith_lower("embed")) | |
+ error(Twine("Invalid option ") + Arg); | |
+ Config->Manifest = Configuration::Embed; | |
+ Arg = Arg.substr(strlen("embed")); | |
+ if (Arg.empty()) | |
+ return; | |
+ if (!Arg.startswith_lower(",id=")) | |
+ error(Twine("Invalid option ") + Arg); | |
+ Arg = Arg.substr(strlen(",id=")); | |
+ if (Arg.getAsInteger(0, Config->ManifestID)) | |
+ error(Twine("Invalid option ") + Arg); | |
+} | |
+ | |
+// Parses a string in the form of "level=<string>|uiAccess=<string>|NO". | |
+// Results are directly written to Config. | |
+void parseManifestUAC(StringRef Arg) { | |
+ if (Arg.equals_lower("no")) { | |
+ Config->ManifestUAC = false; | |
+ return; | |
+ } | |
+ for (;;) { | |
+ Arg = Arg.ltrim(); | |
+ if (Arg.empty()) | |
+ return; | |
+ if (Arg.startswith_lower("level=")) { | |
+ Arg = Arg.substr(strlen("level=")); | |
+ std::tie(Config->ManifestLevel, Arg) = Arg.split(" "); | |
+ continue; | |
+ } | |
+ if (Arg.startswith_lower("uiaccess=")) { | |
+ Arg = Arg.substr(strlen("uiaccess=")); | |
+ std::tie(Config->ManifestUIAccess, Arg) = Arg.split(" "); | |
+ continue; | |
+ } | |
+ error(Twine("Invalid option ") + Arg); | |
+ } | |
+} | |
+ | |
+// Quote each line with "". Existing double-quote is converted | |
+// to two double-quotes. | |
+static void quoteAndPrint(raw_ostream &Out, StringRef S) { | |
+ while (!S.empty()) { | |
+ StringRef Line; | |
+ std::tie(Line, S) = S.split("\n"); | |
+ if (Line.empty()) | |
+ continue; | |
+ Out << '\"'; | |
+ for (int I = 0, E = Line.size(); I != E; ++I) { | |
+ if (Line[I] == '\"') { | |
+ Out << "\"\""; | |
+ } else { | |
+ Out << Line[I]; | |
+ } | |
+ } | |
+ Out << "\"\n"; | |
+ } | |
+} | |
+ | |
+// Create a manifest file contents. | |
+static std::string createManifestXml() { | |
+ std::string S; | |
+ llvm::raw_string_ostream OS(S); | |
+ // Emit the XML. Note that we do *not* verify that the XML attributes are | |
+ // syntactically correct. This is intentional for link.exe compatibility. | |
+ OS << "<?xml version=\"1.0\" standalone=\"yes\"?>\n" | |
+ << "<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\"\n" | |
+ << " manifestVersion=\"1.0\">\n"; | |
+ if (Config->ManifestUAC) { | |
+ OS << " <trustInfo>\n" | |
+ << " <security>\n" | |
+ << " <requestedPrivileges>\n" | |
+ << " <requestedExecutionLevel level=" << Config->ManifestLevel | |
+ << " uiAccess=" << Config->ManifestUIAccess << "/>\n" | |
+ << " </requestedPrivileges>\n" | |
+ << " </security>\n" | |
+ << " </trustInfo>\n"; | |
+ if (!Config->ManifestDependency.empty()) { | |
+ OS << " <dependency>\n" | |
+ << " <dependentAssembly>\n" | |
+ << " <assemblyIdentity " << Config->ManifestDependency << " />\n" | |
+ << " </dependentAssembly>\n" | |
+ << " </dependency>\n"; | |
+ } | |
+ } | |
+ OS << "</assembly>\n"; | |
+ OS.flush(); | |
+ return S; | |
+} | |
+ | |
+// Create a resource file containing a manifest XML. | |
+std::unique_ptr<MemoryBuffer> createManifestRes() { | |
+ std::error_code EC; | |
+ error(EC, "failed to create manifest Res"); | |
+} | |
+ | |
+void createSideBySideManifest() { | |
+ std::string Path = Config->ManifestFile; | |
+ if (Path == "") | |
+ Path = (Twine(Config->OutputFile) + ".manifest").str(); | |
+ std::error_code EC; | |
+ llvm::raw_fd_ostream Out(Path, EC, llvm::sys::fs::F_Text); | |
+ error(EC, "failed to create manifest"); | |
+ Out << createManifestXml(); | |
+} | |
+ | |
+// Parse a string in the form of | |
+// "<name>[=<internalname>][,@ordinal[,NONAME]][,DATA][,PRIVATE]". | |
+// Used for parsing /export arguments. | |
+Export parseExport(StringRef Arg) { | |
+ Export E; | |
+ StringRef Rest; | |
+ std::tie(E.Name, Rest) = Arg.split(","); | |
+ if (E.Name.empty()) | |
+ goto err; | |
+ if (E.Name.find('=') != StringRef::npos) { | |
+ std::tie(E.ExtName, E.Name) = E.Name.split("="); | |
+ if (E.Name.empty()) | |
+ goto err; | |
+ } | |
+ | |
+ while (!Rest.empty()) { | |
+ StringRef Tok; | |
+ std::tie(Tok, Rest) = Rest.split(","); | |
+ if (Tok.equals_lower("noname")) { | |
+ if (E.Ordinal == 0) | |
+ goto err; | |
+ E.Noname = true; | |
+ continue; | |
+ } | |
+ if (Tok.equals_lower("data")) { | |
+ E.Data = true; | |
+ continue; | |
+ } | |
+ if (Tok.equals_lower("private")) { | |
+ E.Private = true; | |
+ continue; | |
+ } | |
+ if (Tok.startswith("@")) { | |
+ int32_t Ord; | |
+ if (Tok.substr(1).getAsInteger(0, Ord)) | |
+ goto err; | |
+ if (Ord <= 0 || 65535 < Ord) | |
+ goto err; | |
+ E.Ordinal = Ord; | |
+ continue; | |
+ } | |
+ goto err; | |
+ } | |
+ return E; | |
+ | |
+err: | |
+ error(Twine("invalid /export: ") + Arg); | |
+} | |
+ | |
+// Performs error checking on all /export arguments. | |
+// It also sets ordinals. | |
+void fixupExports() { | |
+ // Symbol ordinals must be unique. | |
+ std::set<uint16_t> Ords; | |
+ for (Export &E : Config->Exports) { | |
+ if (E.Ordinal == 0) | |
+ continue; | |
+ if (!Ords.insert(E.Ordinal).second) | |
+ error(Twine("duplicate export ordinal: ") + E.Name); | |
+ } | |
+ | |
+ for (Export &E : Config->Exports) { | |
+ if (!E.ExtName.empty()) { | |
+ E.ExtDLLName = E.ExtName; | |
+ E.ExtLibName = E.ExtName; | |
+ continue; | |
+ } | |
+ StringRef S = E.Sym->repl()->getName(); | |
+ if (Config->Machine == I386 && S.startswith("_")) { | |
+ E.ExtDLLName = S.substr(1).split('@').first; | |
+ E.ExtLibName = S.substr(1); | |
+ continue; | |
+ } | |
+ E.ExtDLLName = S; | |
+ E.ExtLibName = S; | |
+ } | |
+ | |
+ // Uniquefy by name. | |
+ std::map<StringRef, Export *> Map; | |
+ std::vector<Export> V; | |
+ for (Export &E : Config->Exports) { | |
+ auto Pair = Map.insert(std::make_pair(E.ExtLibName, &E)); | |
+ bool Inserted = Pair.second; | |
+ if (Inserted) { | |
+ V.push_back(E); | |
+ continue; | |
+ } | |
+ Export *Existing = Pair.first->second; | |
+ if (E == *Existing || E.Name != Existing->Name) | |
+ continue; | |
+ llvm::errs() << "warning: duplicate /export option: " << E.Name << "\n"; | |
+ } | |
+ Config->Exports = std::move(V); | |
+ | |
+ // Sort by name. | |
+ std::sort(Config->Exports.begin(), Config->Exports.end(), | |
+ [](const Export &A, const Export &B) { | |
+ return A.ExtDLLName < B.ExtDLLName; | |
+ }); | |
+} | |
+ | |
+void assignExportOrdinals() { | |
+ // Assign unique ordinals if default (= 0). | |
+ uint16_t Max = 0; | |
+ for (Export &E : Config->Exports) | |
+ Max = std::max(Max, E.Ordinal); | |
+ for (Export &E : Config->Exports) | |
+ if (E.Ordinal == 0) | |
+ E.Ordinal = ++Max; | |
+} | |
+ | |
+// Parses a string in the form of "key=value" and check | |
+// if value matches previous values for the same key. | |
+void checkFailIfMismatch(StringRef Arg) { | |
+ StringRef K, V; | |
+ std::tie(K, V) = Arg.split('='); | |
+ if (K.empty() || V.empty()) | |
+ error(Twine("/failifmismatch: invalid argument: ") + Arg); | |
+ StringRef Existing = Config->MustMatch[K]; | |
+ if (!Existing.empty() && V != Existing) | |
+ error(Twine("/failifmismatch: mismatch detected: ") + Existing + " and " + | |
+ V + " for key " + K); | |
+ Config->MustMatch[K] = V; | |
+} | |
+ | |
+// Convert Windows resource files (.res files) to a .obj file | |
+// using cvtres.exe. | |
+std::unique_ptr<MemoryBuffer> | |
+convertResToCOFF(const std::vector<MemoryBufferRef> &MBs) { | |
+ std::error_code EC; | |
+ error(EC, "failed to convert Res to COFF"); | |
+} | |
+ | |
+static std::string writeToTempFile(StringRef Contents) { | |
+ SmallString<128> Path; | |
+ int FD; | |
+ if (llvm::sys::fs::createTemporaryFile("tmp", "def", FD, Path)) { | |
+ llvm::errs() << "failed to create a temporary file\n"; | |
+ return ""; | |
+ } | |
+ llvm::raw_fd_ostream OS(FD, /*shouldClose*/ true); | |
+ OS << Contents; | |
+ return Path.str(); | |
+} | |
+ | |
+/// Creates a .def file containing the list of exported symbols. | |
+static std::string createModuleDefinitionFile() { | |
+ std::string S; | |
+ llvm::raw_string_ostream OS(S); | |
+ OS << "LIBRARY \"" << llvm::sys::path::filename(Config->OutputFile) << "\"\n" | |
+ << "EXPORTS\n"; | |
+ for (Export &E : Config->Exports) { | |
+ OS << " " << E.ExtLibName; | |
+ if (E.Ordinal > 0) | |
+ OS << " @" << E.Ordinal; | |
+ if (E.Noname) | |
+ OS << " NONAME"; | |
+ if (E.Data) | |
+ OS << " DATA"; | |
+ if (E.Private) | |
+ OS << " PRIVATE"; | |
+ OS << "\n"; | |
+ } | |
+ OS.flush(); | |
+ return S; | |
+} | |
+ | |
+// Creates a .def file and runs lib.exe on it to create an import library. | |
+void writeImportLibrary() { | |
+ std::error_code EC; | |
+ error(EC, "failed to write Import Library"); | |
+} | |
+ | |
+void touchFile(StringRef Path) { | |
+ int FD; | |
+ std::error_code EC = sys::fs::openFileForWrite(Path, FD, sys::fs::F_Append); | |
+ error(EC, "failed to create a file"); | |
+ sys::Process::SafelyCloseFileDescriptor(FD); | |
+} | |
+ | |
+// Create OptTable | |
+ | |
+// Create prefix string literals used in Options.td | |
+#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; | |
+#include "GNUOptions.inc" | |
+#undef PREFIX | |
+ | |
+// Create table mapping all options defined in Options.td | |
+static const llvm::opt::OptTable::Info infoTable[] = { | |
+#define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X6, X7, X8, X9, X10) \ | |
+ { \ | |
+ X1, X2, X9, X10, OPT_##ID, llvm::opt::Option::KIND##Class, X8, X7, \ | |
+ OPT_##GROUP, OPT_##ALIAS, X6 \ | |
+ }, | |
+#include "GNUOptions.inc" | |
+#undef OPTION | |
+}; | |
+ | |
+class COFFOptTable : public llvm::opt::OptTable { | |
+public: | |
+ COFFOptTable() : OptTable(infoTable, llvm::array_lengthof(infoTable), true) {} | |
+}; | |
+ | |
+// Parses a given list of options. | |
+llvm::opt::InputArgList ArgParser::parse(ArrayRef<const char *> ArgsArr) { | |
+ // First, replace respnose files (@<file>-style options). | |
+ std::vector<const char *> Argv = replaceResponseFiles(ArgsArr); | |
+ | |
+ // Make InputArgList from string vectors. | |
+ COFFOptTable Table; | |
+ unsigned MissingIndex; | |
+ unsigned MissingCount; | |
+ llvm::opt::InputArgList Args = | |
+ Table.ParseArgs(Argv, MissingIndex, MissingCount); | |
+ if (MissingCount) | |
+ error(Twine("missing arg value for \"") + Args.getArgString(MissingIndex) + | |
+ "\", expected " + Twine(MissingCount) + | |
+ (MissingCount == 1 ? " argument." : " arguments.")); | |
+ for (auto *Arg : Args.filtered(OPT_UNKNOWN)) | |
+ llvm::errs() << "ignoring unknown argument: " << Arg->getSpelling() << "\n"; | |
+ return Args; | |
+} | |
+ | |
+std::vector<const char *> ArgParser::tokenize(StringRef S) { | |
+ SmallVector<const char *, 16> Tokens; | |
+ StringSaver Saver(AllocAux); | |
+ llvm::cl::TokenizeWindowsCommandLine(S, Saver, Tokens); | |
+ return std::vector<const char *>(Tokens.begin(), Tokens.end()); | |
+} | |
+ | |
+// Creates a new command line by replacing options starting with '@' | |
+// character. '@<filename>' is replaced by the file's contents. | |
+std::vector<const char *> | |
+ArgParser::replaceResponseFiles(std::vector<const char *> Argv) { | |
+ SmallVector<const char *, 256> Tokens(Argv.data(), Argv.data() + Argv.size()); | |
+ StringSaver Saver(AllocAux); | |
+ ExpandResponseFiles(Saver, TokenizeWindowsCommandLine, Tokens); | |
+ return std::vector<const char *>(Tokens.begin(), Tokens.end()); | |
+} | |
+ | |
+void printHelp(const char *Argv0) { | |
+ COFFOptTable Table; | |
+ Table.PrintHelp(llvm::outs(), Argv0, "LLVM Linker", false); | |
+} | |
+ | |
+} // namespace coff | |
+} // namespace lld | |
diff --git a/COFF/GNUOptions.td b/COFF/GNUOptions.td | |
new file mode 100644 | |
index 0000000..567aecf | |
--- /dev/null | |
+++ b/COFF/GNUOptions.td | |
@@ -0,0 +1,8 @@ | |
+include "llvm/Option/OptParser.td" | |
+ | |
+//===----------------------------------------------------------------------===// | |
+/// Utility Functions | |
+//===----------------------------------------------------------------------===// | |
+ | |
+def output : Separate<["-"], "o">, MetaVarName<"<path>">, | |
+ HelpText<"Path to file to write output">; | |
diff --git a/lib/Driver/UniversalDriver.cpp b/lib/Driver/UniversalDriver.cpp | |
index 45bcbc8..8c1fcb4 100644 | |
--- a/lib/Driver/UniversalDriver.cpp | |
+++ b/lib/Driver/UniversalDriver.cpp | |
@@ -94,6 +94,19 @@ static Flavor strToFlavor(StringRef str) { | |
.Default(Flavor::invalid); | |
} | |
+static bool isPETarget(const llvm::opt::InputArgList &parsedArgs) { | |
+ llvm::opt::Arg *argMachine = parsedArgs.getLastArg(OPT_m); | |
+ if (!argMachine) | |
+ return false; | |
+ if (argMachine->containsValue("i386pe")) | |
+ return true; | |
+ if (argMachine->containsValue("i386pep")) | |
+ return true; | |
+ if (argMachine->containsValue("thumb2pe")) | |
+ return true; | |
+ return false; | |
+} | |
+ | |
static ProgramNameParts parseProgramName(StringRef programName) { | |
SmallVector<StringRef, 3> components; | |
llvm::SplitString(programName, components, "-"); | |
@@ -195,6 +208,12 @@ bool UniversalDriver::link(llvm::MutableArrayRef<const char *> args, | |
return true; | |
} | |
+ //Override gnu for pe targets to use coff | |
+ if(isPETarget(parsedArgs)) { | |
+ coff::link(args); | |
+ return true; | |
+ } | |
+ | |
Flavor flavor = getFlavor(args, parsedArgs); | |
// Switch to appropriate driver. | |
diff --git a/lib/Driver/UniversalDriverOptions.td b/lib/Driver/UniversalDriverOptions.td | |
index 14abc9c..38e620b 100644 | |
--- a/lib/Driver/UniversalDriverOptions.td | |
+++ b/lib/Driver/UniversalDriverOptions.td | |
@@ -11,6 +11,10 @@ def core : Flag<["-"], "core">, | |
def target: Separate<["-"], "target">, | |
HelpText<"Select the target">; | |
+// Used to override gnu pe targets | |
+def m : Separate<["-"], "m">, MetaVarName<"<emulation>">, | |
+ HelpText<"Select target emulation">; | |
+ | |
def version: Flag<["-"], "version">, | |
HelpText<"Display the version">; | |
-- | |
2.5.0 | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment