Index: CMakeLists.txt =================================================================== --- CMakeLists.txt +++ CMakeLists.txt @@ -221,4 +221,5 @@ add_subdirectory(docs) add_subdirectory(COFF) add_subdirectory(ELF) +add_subdirectory(MinGW) Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -113,12 +113,8 @@ .Case("elf_iamcu", {ELF32LEKind, EM_IAMCU}) .Default({ELFNoneKind, EM_NONE}); - if (Ret.first == ELFNoneKind) { - if (S == "i386pe" || S == "i386pep" || S == "thumb2pe") - error("Windows targets are not supported on the ELF frontend: " + Emul); - else - error("unknown emulation: " + Emul); - } + if (Ret.first == ELFNoneKind) + error("unknown emulation: " + Emul); return std::make_tuple(Ret.first, Ret.second, OSABI); } Index: MinGW/CMakeLists.txt =================================================================== --- /dev/null +++ MinGW/CMakeLists.txt @@ -0,0 +1,20 @@ +set(LLVM_TARGET_DEFINITIONS Options.td) +tablegen(LLVM Options.inc -gen-opt-parser-defs) +add_public_tablegen_target(ShimOptionsTableGen) + +if(NOT LLD_BUILT_STANDALONE) + set(tablegen_deps intrinsics_gen) +endif() + +add_lld_library(lldMinGW + Driver.cpp + + LINK_LIBS + lldConfig + lldCore + ${LLVM_PTHREAD_LIB} + + DEPENDS + ShimOptionsTableGen + ${tablegen_deps} +) Index: MinGW/Driver.cpp =================================================================== --- /dev/null +++ MinGW/Driver.cpp @@ -0,0 +1,236 @@ +//===- MinGW/Driver.cpp ---------------------------------------------------===// +// +// The LLVM Linker +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// GNU ld style linker driver for COFF currently supporting mingw-w64. +/// +//===----------------------------------------------------------------------===// + +#include "lld/Driver/Driver.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/Triple.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/Option.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/StringSaver.h" + +#if !defined(_MSC_VER) && !defined(__MINGW32__) +#include +#endif + +using namespace lld; +using namespace llvm; + +namespace lld { +namespace mingw { +namespace { + +// Create OptTable +enum { + OPT_INVALID = 0, +#define OPTION(_1, _2, ID, _4, _5, _6, _7, _8, _9, _10, _11, _12) OPT_##ID, +#include "Options.inc" +#undef OPTION +}; + +// Create prefix string literals used in Options.td +#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; +#include "Options.inc" +#undef PREFIX + +// Create table mapping all options defined in Options.td +static const opt::OptTable::Info infoTable[] = { +#define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X7, X8, X9, X10, X11, X12) \ + {X1, X2, X10, X11, OPT_##ID, opt::Option::KIND##Class, \ + X9, X8, OPT_##GROUP, OPT_##ALIAS, X7, X12}, +#include "Options.inc" +#undef OPTION +}; + +class COFFLdOptTable : public opt::OptTable { +public: + COFFLdOptTable() : OptTable(infoTable, false) {} + opt::InputArgList parse(ArrayRef Argv); +}; + +} // namespace + +BumpPtrAllocator BAlloc; +StringSaver Saver{BAlloc}; +static SmallVector LinkArgs; +static std::vector SearchPaths; +static bool files = false; + +static void error(const Twine &Msg) { + errs() << Msg << "\n"; + llvm_shutdown(); + outs().flush(); + errs().flush(); + _exit(1); +} + +static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &Args) { + if (auto *Arg = Args.getLastArg(OPT_rsp_quoting)) { + StringRef S = Arg->getValue(); + if (S != "windows" && S != "posix") + error("invalid response file quoting: " + S); + if (S == "windows") + return cl::TokenizeWindowsCommandLine; + return cl::TokenizeGNUCommandLine; + } + if (Triple(sys::getProcessTriple()).getOS() == Triple::Win32) + return cl::TokenizeWindowsCommandLine; + return cl::TokenizeGNUCommandLine; +} + +// Find a file by concatenating given paths. +static Optional findFile(StringRef Path1, const Twine &Path2) { + SmallString<128> S; + sys::path::append(S, Path1, Path2); + if (sys::fs::exists(S)) + return S.str().str(); + return None; +} + +Optional findFromSearchPaths(StringRef Path) { + for (StringRef Dir : SearchPaths) + if (Optional S = findFile(Dir, Path)) + return S; + return None; +} + +// This is for -lfoo. We'll look for libfoo.dll.a or libfoo.a from search paths. +Optional searchLibrary(StringRef Name, bool IgnoreShared = false) { + if (Name.startswith(":")) + return findFromSearchPaths(Name.substr(1)); + for (StringRef Dir : SearchPaths) { + if (!IgnoreShared) + if (Optional S = findFile(Dir, "lib" + Name + ".dll.a")) + return S; + if (Optional S = findFile(Dir, "lib" + Name + ".a")) + return S; + } + return None; +} + +void addFile(StringRef Path) { + LinkArgs.push_back(Saver.save(Path).data()); + files = true; +} + +// Add a given library by searching it from input search paths. +void addLibrary(StringRef Name, bool StaticOnly) { + if (Optional Path = searchLibrary(Name, StaticOnly)) + addFile(*Path); + else + error("unable to find library -l" + Name); +} + +void createFiles(opt::InputArgList &Args) { + for (auto *Arg : Args) + switch (Arg->getOption().getID()) { + case OPT_l: + addLibrary(Arg->getValue(), Args.hasArg(OPT_Bstatic)); + break; + case OPT_INPUT: + addFile(Arg->getValue()); + break; + } + if (!files) + error("no input files"); +} + +static StringRef getString(opt::InputArgList &Args, unsigned Key, + StringRef Default = "") { + if (auto *Arg = Args.getLastArg(Key)) + return Arg->getValue(); + return Default; +} + +static std::vector getArgs(opt::InputArgList &Args, int Id) { + std::vector V; + for (auto *Arg : Args.filtered(Id)) + V.push_back(Arg->getValue()); + return V; +} + +static void forward(opt::InputArgList &Args, unsigned Key, StringRef outArg) { + StringRef S = getString(Args, Key); + if (S.size()) + LinkArgs.push_back(Saver.save(Twine("-" + outArg + ":" + S)).data()); +} + +static void forwardValue(opt::InputArgList &Args, unsigned Key, + StringRef cmpArg, StringRef outArg, + StringRef outValueArg) { + StringRef S = getString(Args, Key); + if (S == cmpArg) + LinkArgs.push_back( + Saver.save(Twine("-" + outArg + ":" + outValueArg)).data()); +} + +// Parses a given list of options. +opt::InputArgList COFFLdOptTable::parse(ArrayRef Argv) { + // Make InputArgList from string vectors. + unsigned MissingIndex; + unsigned MissingCount; + SmallVector Vec(Argv.data(), Argv.data() + Argv.size()); + + opt::InputArgList Args = this->ParseArgs(Vec, MissingIndex, MissingCount); + + // Expand response files (arguments in the form of @) + // and then parse the argument again. + cl::ExpandResponseFiles(Saver, getQuotingStyle(Args), Vec); + Args = this->ParseArgs(Vec, MissingIndex, MissingCount); + + if (MissingCount) + error(Twine(Args.getArgString(MissingIndex)) + ": missing argument"); + + for (auto *Arg : Args.filtered(OPT_UNKNOWN)) + error("unknown argument: " + Arg->getSpelling()); + return Args; +} + +bool link(ArrayRef ArgsArr, raw_ostream &Diag) { + COFFLdOptTable Parser; + opt::InputArgList Args = Parser.parse(ArgsArr.slice(1)); + LinkArgs.push_back(ArgsArr[0]); + + forwardValue(Args, OPT_m, "i386pe", "MACHINE", "X86"); + forwardValue(Args, OPT_m, "i386pep", "MACHINE", "X64"); + forwardValue(Args, OPT_m, "thumb2pe", "MACHINE", "ARM"); + forwardValue(Args, OPT_m, "arm64pe", "MACHINE", "ARM64"); + + forward(Args, OPT_o, "OUT"); + forward(Args, OPT_entry, "ENTRY"); + forward(Args, OPT_subs, "SUBSYSTEM"); + forward(Args, OPT_shared, "DLL"); + forward(Args, OPT_outlib, "IMPLIB"); + forward(Args, OPT_stack, "STACK"); + + SearchPaths = getArgs(Args, OPT_L); + createFiles(Args); + + // handle __image_base__ + auto M = Args.getLastArg(OPT_m); + if (M && StringRef(M->getValue()) == "i386pe") + LinkArgs.push_back("/alternatename:__image_base__=___ImageBase"); + else + LinkArgs.push_back("/alternatename:__image_base__=__ImageBase"); + + return coff::link(LinkArgs); +} + +} // namespace mingw +} // namespace lld Index: MinGW/Options.td =================================================================== --- /dev/null +++ MinGW/Options.td @@ -0,0 +1,36 @@ +include "llvm/Option/OptParser.td" + +class F: Flag<["--", "-"], name>; +class J: Joined<["--", "-"], name>; +class S: Separate<["--", "-"], name>; + +def L: JoinedOrSeparate<["-"], "L">, MetaVarName<"">, + HelpText<"Add a directory to the library search path">; +def entry: S<"entry">, MetaVarName<"">, + HelpText<"Name of entry point symbol">; +def m: JoinedOrSeparate<["-"], "m">, HelpText<"Set target emulation">; +def o: JoinedOrSeparate<["-"], "o">, MetaVarName<"">, + HelpText<"Path to file to write output">; +def l: JoinedOrSeparate<["-"], "l">, MetaVarName<"">, + HelpText<"Root name of library to use">; +def shared: F<"shared">, HelpText<"Build a shared object">; +def subs: Separate<["--"], "subsystem">, HelpText<"Specify subsystem">; +def stack: Flag<["--"], "stack">, Alias; +def outlib: Separate<["--"], "out-implib">, HelpText<"Import library name">; + +// Currently stubs to avoid errors +def Bdynamic: F<"Bdynamic">, HelpText<"Link against shared libraries">; +def Bstatic: F<"Bstatic">, HelpText<"Do not link against shared libraries">; +def major_image_version: Separate<["--"], "major-image-version">; +def minor_image_version: Separate<["--"], "minor-image-version">; +def O: Joined<["-"], "O">, HelpText<"Optimize output file size">; +def v: Flag<["-"], "v">, HelpText<"Display the version number">; +def verbose: F<"verbose">, HelpText<"Verbose mode">; +def version: F<"version">, HelpText<"Display the version number and exit">; + +// Alias +def alias_entry_e: JoinedOrSeparate<["-"], "e">, Alias; + +// Style +def rsp_quoting: J<"rsp-quoting=">, + HelpText<"Quoting style for response files. Values supported: windows|posix">; Index: include/lld/Driver/Driver.h =================================================================== --- include/lld/Driver/Driver.h +++ include/lld/Driver/Driver.h @@ -19,6 +19,11 @@ llvm::raw_ostream &Diag = llvm::errs()); } +namespace mingw { +bool link(llvm::ArrayRef Args, + llvm::raw_ostream &Diag = llvm::errs()); +} + namespace elf { bool link(llvm::ArrayRef Args, bool CanExitEarly, llvm::raw_ostream &Diag = llvm::errs()); Index: tools/lld/CMakeLists.txt =================================================================== --- tools/lld/CMakeLists.txt +++ tools/lld/CMakeLists.txt @@ -10,6 +10,7 @@ lldDriver lldCOFF lldELF + lldMinGW ) install(TARGETS lld Index: tools/lld/lld.cpp =================================================================== --- tools/lld/lld.cpp +++ tools/lld/lld.cpp @@ -49,6 +49,16 @@ .Default(Invalid); } +static bool isPETarget(const std::vector &V) { + for (auto It = V.begin(); It + 1 != V.end(); ++It) { + if (StringRef(*It) != "-m") + continue; + StringRef S = *(It + 1); + return S == "i386pe" || S == "i386pep" || S == "thumb2pe" || S == "arm64pe"; + } + return false; +} + static Flavor parseProgname(StringRef Progname) { #if __APPLE__ // Use Darwin driver for "ld" on Darwin. @@ -101,6 +111,8 @@ std::vector Args(Argv, Argv + Argc); switch (parseFlavor(Args)) { case Gnu: + if (isPETarget(Args)) + return !mingw::link(Args); return !elf::link(Args, true); case WinLink: return !coff::link(Args);