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,204 @@ +//===- 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" + +#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 + +static std::vector LinkArgs; +static std::vector SearchPaths; + +static void error(const Twine &Msg) { + errs() << Msg << "\n"; + llvm_shutdown(); + exit(1); +} + +// 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 StaticOnly) { + if (Name.startswith(":")) + return findFromSearchPaths(Name.substr(1)); + for (StringRef Dir : SearchPaths) { + if (!StaticOnly) + if (Optional S = findFile(Dir, "lib" + Name + ".dll.a")) + return S; + if (Optional S = findFile(Dir, "lib" + Name + ".a")) + return S; + } + return None; +} + +// Add a given library by searching it from input search paths. +void addLibrary(StringRef Name, bool StaticOnly) { + if (Optional Path = searchLibrary(Name, StaticOnly)) + LinkArgs.push_back(*Path); + else + error("unable to find library -l" + Name); +} + +void createFiles(opt::InputArgList &Args) { + for (auto *Arg : Args) { + switch (Arg->getOption().getUnaliasedOption().getID()) { + case OPT_l: + addLibrary(Arg->getValue(), Args.hasArg(OPT_Bstatic)); + break; + case OPT_INPUT: + LinkArgs.push_back(Arg->getValue()); + break; + } + } +} + +static void forward(opt::InputArgList &Args, unsigned Key, + const std::string &OutArg, std::string Default = "") { + StringRef S = Args.getLastArgValue(Key); + if (!S.empty()) + LinkArgs.push_back(std::string("-").append(OutArg).append(":").append(S)); + else if (!Default.empty()) + LinkArgs.push_back( + std::string("-").append(OutArg).append(":").append(Default)); +} + +static void forwardValue(opt::InputArgList &Args, unsigned Key, + const std::string &CmpArg, const std::string &OutArg) { + StringRef S = Args.getLastArgValue(Key); + if (S == CmpArg) + LinkArgs.push_back(std::string("-").append(OutArg)); +} + +static bool convertValue(opt::InputArgList &Args, unsigned Key, + StringRef OutArg) { + if (Args.hasArg(Key)) { + LinkArgs.push_back(std::string("-").append(OutArg)); + return true; + } + return false; +} + +opt::InputArgList COFFLdOptTable::parse(ArrayRef Argv) { + unsigned MissingIndex; + unsigned MissingCount; + SmallVector Vec(Argv.data(), Argv.data() + Argv.size()); + opt::InputArgList Args = this->ParseArgs(Vec, MissingIndex, MissingCount); + if (MissingCount) + error(Twine(Args.getArgString(MissingIndex)) + ": missing argument"); + if (!Args.hasArgNoClaim(OPT_INPUT) && !Args.hasArgNoClaim(OPT_l)) + error("no input files"); + 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", + convertValue(Args, OPT_shared, "dll") ? "a.exe" : "a.dll"); + forward(Args, OPT_entry, "entry"); + forward(Args, OPT_subs, "subsystem"); + forward(Args, OPT_outlib, "implib"); + forward(Args, OPT_stack, "stack"); + + for (auto *Arg : Args.filtered(OPT_L)) + SearchPaths.push_back(Arg->getValue()); + + createFiles(Args); + + // handle __image_base__ + if (Args.getLastArgValue(OPT_m) == "i386pe") + LinkArgs.push_back("/alternatename:__image_base__=___ImageBase"); + else + LinkArgs.push_back("/alternatename:__image_base__=__ImageBase"); + + // repack vector of strings to vector of const char pointers for coff::link + std::vector Vec; + for (const std::string &S : LinkArgs) + Vec.push_back(S.c_str()); + return coff::link(Vec); +} + +} // namespace mingw +} // namespace lld Index: MinGW/Options.td =================================================================== --- /dev/null +++ MinGW/Options.td @@ -0,0 +1,34 @@ +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 enable_auto_image_base: Flag<["--"], "enable-auto-image-base">; +def full_shutdown: Flag<["--"], "full-shutdown">; +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; 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: test/MinGW/Inputs/ret42.yaml =================================================================== --- /dev/null +++ test/MinGW/Inputs/ret42.yaml @@ -0,0 +1,45 @@ +--- !COFF +header: + Machine: IMAGE_FILE_MACHINE_AMD64 + Characteristics: [] +sections: + - Name: '.text$mn' + Characteristics: [ IMAGE_SCN_CNT_CODE, IMAGE_SCN_MEM_EXECUTE, IMAGE_SCN_MEM_READ ] + Alignment: 16 + SectionData: B82A000000C3 + - Name: .data + Characteristics: [ IMAGE_SCN_CNT_INITIALIZED_DATA, IMAGE_SCN_MEM_READ, IMAGE_SCN_MEM_WRITE ] + Alignment: 16 + SectionData: '' +symbols: + - Name: '.text$mn' + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 6 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: .data + Value: 0 + SectionNumber: 2 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_NULL + StorageClass: IMAGE_SYM_CLASS_STATIC + SectionDefinition: + Length: 0 + NumberOfRelocations: 0 + NumberOfLinenumbers: 0 + CheckSum: 0 + Number: 0 + - Name: main + Value: 0 + SectionNumber: 1 + SimpleType: IMAGE_SYM_TYPE_NULL + ComplexType: IMAGE_SYM_DTYPE_FUNCTION + StorageClass: IMAGE_SYM_CLASS_EXTERNAL +... Index: test/MinGW/output.test =================================================================== --- /dev/null +++ test/MinGW/output.test @@ -0,0 +1,13 @@ +# RUN: yaml2obj < %p/Inputs/ret42.yaml > %t.obj + +# RUN: mkdir -p %T/out/tmp +# RUN: cp %t.obj %T/out/out1.obj + +# RUN: rm -f a.exe a.dll +# RUN: ld.lld -m i386pep --entry main %T/out/out1.obj +# RUN: ld.lld -m i386pep -shared --entry main %T/out/out1.obj + +# RUN: llvm-readobj a.exe | FileCheck %s +# RUN: llvm-readobj a.dll | FileCheck %s + +CHECK: File: 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);