10b57cec5SDimitry Andric //===- DlltoolDriver.cpp - dlltool.exe-compatible driver ------------------===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric // 90b57cec5SDimitry Andric // Defines an interface to a dlltool.exe-compatible driver. 100b57cec5SDimitry Andric // 110b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 120b57cec5SDimitry Andric 130b57cec5SDimitry Andric #include "llvm/ToolDrivers/llvm-dlltool/DlltoolDriver.h" 14349cc55cSDimitry Andric #include "llvm/ADT/StringSwitch.h" 150b57cec5SDimitry Andric #include "llvm/Object/COFF.h" 160b57cec5SDimitry Andric #include "llvm/Object/COFFImportFile.h" 170b57cec5SDimitry Andric #include "llvm/Object/COFFModuleDefinition.h" 180b57cec5SDimitry Andric #include "llvm/Option/Arg.h" 190b57cec5SDimitry Andric #include "llvm/Option/ArgList.h" 205f757f3fSDimitry Andric #include "llvm/Option/OptTable.h" 210b57cec5SDimitry Andric #include "llvm/Option/Option.h" 220b57cec5SDimitry Andric #include "llvm/Support/Path.h" 2306c3fb27SDimitry Andric #include "llvm/TargetParser/Host.h" 240b57cec5SDimitry Andric 25bdd1243dSDimitry Andric #include <optional> 260b57cec5SDimitry Andric #include <vector> 270b57cec5SDimitry Andric 280b57cec5SDimitry Andric using namespace llvm; 290b57cec5SDimitry Andric using namespace llvm::object; 300b57cec5SDimitry Andric using namespace llvm::COFF; 310b57cec5SDimitry Andric 320b57cec5SDimitry Andric namespace { 330b57cec5SDimitry Andric 340b57cec5SDimitry Andric enum { 350b57cec5SDimitry Andric OPT_INVALID = 0, 365f757f3fSDimitry Andric #define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__), 370b57cec5SDimitry Andric #include "Options.inc" 380b57cec5SDimitry Andric #undef OPTION 390b57cec5SDimitry Andric }; 400b57cec5SDimitry Andric 41bdd1243dSDimitry Andric #define PREFIX(NAME, VALUE) \ 42bdd1243dSDimitry Andric static constexpr StringLiteral NAME##_init[] = VALUE; \ 43bdd1243dSDimitry Andric static constexpr ArrayRef<StringLiteral> NAME(NAME##_init, \ 44bdd1243dSDimitry Andric std::size(NAME##_init) - 1); 450b57cec5SDimitry Andric #include "Options.inc" 460b57cec5SDimitry Andric #undef PREFIX 470b57cec5SDimitry Andric 485f757f3fSDimitry Andric using namespace llvm::opt; 49bdd1243dSDimitry Andric static constexpr opt::OptTable::Info InfoTable[] = { 505f757f3fSDimitry Andric #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__), 510b57cec5SDimitry Andric #include "Options.inc" 520b57cec5SDimitry Andric #undef OPTION 530b57cec5SDimitry Andric }; 540b57cec5SDimitry Andric 55bdd1243dSDimitry Andric class DllOptTable : public opt::GenericOptTable { 560b57cec5SDimitry Andric public: 57bdd1243dSDimitry Andric DllOptTable() : opt::GenericOptTable(InfoTable, false) {} 580b57cec5SDimitry Andric }; 590b57cec5SDimitry Andric 600b57cec5SDimitry Andric // Opens a file. Path has to be resolved already. 61fe6060f1SDimitry Andric std::unique_ptr<MemoryBuffer> openFile(const Twine &Path) { 620b57cec5SDimitry Andric ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> MB = MemoryBuffer::getFile(Path); 630b57cec5SDimitry Andric 640b57cec5SDimitry Andric if (std::error_code EC = MB.getError()) { 650b57cec5SDimitry Andric llvm::errs() << "cannot open file " << Path << ": " << EC.message() << "\n"; 660b57cec5SDimitry Andric return nullptr; 670b57cec5SDimitry Andric } 680b57cec5SDimitry Andric 690b57cec5SDimitry Andric return std::move(*MB); 700b57cec5SDimitry Andric } 710b57cec5SDimitry Andric 72fe6060f1SDimitry Andric MachineTypes getEmulation(StringRef S) { 730b57cec5SDimitry Andric return StringSwitch<MachineTypes>(S) 740b57cec5SDimitry Andric .Case("i386", IMAGE_FILE_MACHINE_I386) 750b57cec5SDimitry Andric .Case("i386:x86-64", IMAGE_FILE_MACHINE_AMD64) 760b57cec5SDimitry Andric .Case("arm", IMAGE_FILE_MACHINE_ARMNT) 770b57cec5SDimitry Andric .Case("arm64", IMAGE_FILE_MACHINE_ARM64) 78*0fca6ea1SDimitry Andric .Case("arm64ec", IMAGE_FILE_MACHINE_ARM64EC) 790b57cec5SDimitry Andric .Default(IMAGE_FILE_MACHINE_UNKNOWN); 800b57cec5SDimitry Andric } 810b57cec5SDimitry Andric 82fe6060f1SDimitry Andric MachineTypes getMachine(Triple T) { 83fe6060f1SDimitry Andric switch (T.getArch()) { 84fe6060f1SDimitry Andric case Triple::x86: 85fe6060f1SDimitry Andric return COFF::IMAGE_FILE_MACHINE_I386; 86fe6060f1SDimitry Andric case Triple::x86_64: 87fe6060f1SDimitry Andric return COFF::IMAGE_FILE_MACHINE_AMD64; 88fe6060f1SDimitry Andric case Triple::arm: 89fe6060f1SDimitry Andric return COFF::IMAGE_FILE_MACHINE_ARMNT; 90fe6060f1SDimitry Andric case Triple::aarch64: 91*0fca6ea1SDimitry Andric return T.isWindowsArm64EC() ? COFF::IMAGE_FILE_MACHINE_ARM64EC 92*0fca6ea1SDimitry Andric : COFF::IMAGE_FILE_MACHINE_ARM64; 93fe6060f1SDimitry Andric default: 94fe6060f1SDimitry Andric return COFF::IMAGE_FILE_MACHINE_UNKNOWN; 95fe6060f1SDimitry Andric } 96fe6060f1SDimitry Andric } 97fe6060f1SDimitry Andric 98fe6060f1SDimitry Andric MachineTypes getDefaultMachine() { 99fe6060f1SDimitry Andric return getMachine(Triple(sys::getDefaultTargetTriple())); 100fe6060f1SDimitry Andric } 101fe6060f1SDimitry Andric 102bdd1243dSDimitry Andric std::optional<std::string> getPrefix(StringRef Argv0) { 103fe6060f1SDimitry Andric StringRef ProgName = llvm::sys::path::stem(Argv0); 104fe6060f1SDimitry Andric // x86_64-w64-mingw32-dlltool -> x86_64-w64-mingw32 105fe6060f1SDimitry Andric // llvm-dlltool -> None 106fe6060f1SDimitry Andric // aarch64-w64-mingw32-llvm-dlltool-10.exe -> aarch64-w64-mingw32 107fe6060f1SDimitry Andric ProgName = ProgName.rtrim("0123456789.-"); 108fe6060f1SDimitry Andric if (!ProgName.consume_back_insensitive("dlltool")) 109bdd1243dSDimitry Andric return std::nullopt; 110fe6060f1SDimitry Andric ProgName.consume_back_insensitive("llvm-"); 111fe6060f1SDimitry Andric ProgName.consume_back_insensitive("-"); 112fe6060f1SDimitry Andric return ProgName.str(); 113fe6060f1SDimitry Andric } 114fe6060f1SDimitry Andric 115*0fca6ea1SDimitry Andric bool parseModuleDefinition(StringRef DefFileName, MachineTypes Machine, 116*0fca6ea1SDimitry Andric bool AddUnderscores, 117*0fca6ea1SDimitry Andric std::vector<COFFShortExport> &Exports, 118*0fca6ea1SDimitry Andric std::string &OutputFile) { 119*0fca6ea1SDimitry Andric std::unique_ptr<MemoryBuffer> MB = openFile(DefFileName); 120*0fca6ea1SDimitry Andric if (!MB) 121*0fca6ea1SDimitry Andric return false; 122*0fca6ea1SDimitry Andric 123*0fca6ea1SDimitry Andric if (!MB->getBufferSize()) { 124*0fca6ea1SDimitry Andric llvm::errs() << "definition file empty\n"; 125*0fca6ea1SDimitry Andric return false; 126*0fca6ea1SDimitry Andric } 127*0fca6ea1SDimitry Andric 128*0fca6ea1SDimitry Andric Expected<COFFModuleDefinition> Def = parseCOFFModuleDefinition( 129*0fca6ea1SDimitry Andric *MB, Machine, /*MingwDef=*/true, AddUnderscores); 130*0fca6ea1SDimitry Andric if (!Def) { 131*0fca6ea1SDimitry Andric llvm::errs() << "error parsing definition\n" 132*0fca6ea1SDimitry Andric << errorToErrorCode(Def.takeError()).message() << "\n"; 133*0fca6ea1SDimitry Andric return false; 134*0fca6ea1SDimitry Andric } 135*0fca6ea1SDimitry Andric 136*0fca6ea1SDimitry Andric if (OutputFile.empty()) 137*0fca6ea1SDimitry Andric OutputFile = std::move(Def->OutputFile); 138*0fca6ea1SDimitry Andric 139*0fca6ea1SDimitry Andric // If ExtName is set (if the "ExtName = Name" syntax was used), overwrite 140*0fca6ea1SDimitry Andric // Name with ExtName and clear ExtName. When only creating an import 141*0fca6ea1SDimitry Andric // library and not linking, the internal name is irrelevant. This avoids 142*0fca6ea1SDimitry Andric // cases where writeImportLibrary tries to transplant decoration from 143*0fca6ea1SDimitry Andric // symbol decoration onto ExtName. 144*0fca6ea1SDimitry Andric for (COFFShortExport &E : Def->Exports) { 145*0fca6ea1SDimitry Andric if (!E.ExtName.empty()) { 146*0fca6ea1SDimitry Andric E.Name = E.ExtName; 147*0fca6ea1SDimitry Andric E.ExtName.clear(); 148*0fca6ea1SDimitry Andric } 149*0fca6ea1SDimitry Andric } 150*0fca6ea1SDimitry Andric 151*0fca6ea1SDimitry Andric Exports = std::move(Def->Exports); 152*0fca6ea1SDimitry Andric return true; 153*0fca6ea1SDimitry Andric } 154*0fca6ea1SDimitry Andric 155fe6060f1SDimitry Andric } // namespace 156fe6060f1SDimitry Andric 1570b57cec5SDimitry Andric int llvm::dlltoolDriverMain(llvm::ArrayRef<const char *> ArgsArr) { 1580b57cec5SDimitry Andric DllOptTable Table; 1590b57cec5SDimitry Andric unsigned MissingIndex; 1600b57cec5SDimitry Andric unsigned MissingCount; 1610b57cec5SDimitry Andric llvm::opt::InputArgList Args = 1620b57cec5SDimitry Andric Table.ParseArgs(ArgsArr.slice(1), MissingIndex, MissingCount); 1630b57cec5SDimitry Andric if (MissingCount) { 1640b57cec5SDimitry Andric llvm::errs() << Args.getArgString(MissingIndex) << ": missing argument\n"; 1650b57cec5SDimitry Andric return 1; 1660b57cec5SDimitry Andric } 1670b57cec5SDimitry Andric 1680b57cec5SDimitry Andric // Handle when no input or output is specified 1690b57cec5SDimitry Andric if (Args.hasArgNoClaim(OPT_INPUT) || 1700b57cec5SDimitry Andric (!Args.hasArgNoClaim(OPT_d) && !Args.hasArgNoClaim(OPT_l))) { 171fe6060f1SDimitry Andric Table.printHelp(outs(), "llvm-dlltool [options] file...", "llvm-dlltool", 1720b57cec5SDimitry Andric false); 173*0fca6ea1SDimitry Andric llvm::outs() << "\nTARGETS: i386, i386:x86-64, arm, arm64, arm64ec\n"; 1740b57cec5SDimitry Andric return 1; 1750b57cec5SDimitry Andric } 1760b57cec5SDimitry Andric 1770b57cec5SDimitry Andric for (auto *Arg : Args.filtered(OPT_UNKNOWN)) 1780b57cec5SDimitry Andric llvm::errs() << "ignoring unknown argument: " << Arg->getAsString(Args) 1790b57cec5SDimitry Andric << "\n"; 1800b57cec5SDimitry Andric 1810b57cec5SDimitry Andric if (!Args.hasArg(OPT_d)) { 1820b57cec5SDimitry Andric llvm::errs() << "no definition file specified\n"; 1830b57cec5SDimitry Andric return 1; 1840b57cec5SDimitry Andric } 1850b57cec5SDimitry Andric 186fe6060f1SDimitry Andric COFF::MachineTypes Machine = getDefaultMachine(); 187bdd1243dSDimitry Andric if (std::optional<std::string> Prefix = getPrefix(ArgsArr[0])) { 188fe6060f1SDimitry Andric Triple T(*Prefix); 189fe6060f1SDimitry Andric if (T.getArch() != Triple::UnknownArch) 190fe6060f1SDimitry Andric Machine = getMachine(T); 191fe6060f1SDimitry Andric } 1920b57cec5SDimitry Andric if (auto *Arg = Args.getLastArg(OPT_m)) 1930b57cec5SDimitry Andric Machine = getEmulation(Arg->getValue()); 1940b57cec5SDimitry Andric 1950b57cec5SDimitry Andric if (Machine == IMAGE_FILE_MACHINE_UNKNOWN) { 1960b57cec5SDimitry Andric llvm::errs() << "unknown target\n"; 1970b57cec5SDimitry Andric return 1; 1980b57cec5SDimitry Andric } 1990b57cec5SDimitry Andric 20006c3fb27SDimitry Andric bool AddUnderscores = !Args.hasArg(OPT_no_leading_underscore); 2010b57cec5SDimitry Andric 202*0fca6ea1SDimitry Andric std::string OutputFile; 203*0fca6ea1SDimitry Andric if (auto *Arg = Args.getLastArg(OPT_D)) 204*0fca6ea1SDimitry Andric OutputFile = Arg->getValue(); 205*0fca6ea1SDimitry Andric 206*0fca6ea1SDimitry Andric std::vector<COFFShortExport> Exports, NativeExports; 207*0fca6ea1SDimitry Andric 208*0fca6ea1SDimitry Andric if (Args.hasArg(OPT_N)) { 209*0fca6ea1SDimitry Andric if (!isArm64EC(Machine)) { 210*0fca6ea1SDimitry Andric llvm::errs() << "native .def file is supported only on arm64ec target\n"; 211*0fca6ea1SDimitry Andric return 1; 212*0fca6ea1SDimitry Andric } 213*0fca6ea1SDimitry Andric if (!parseModuleDefinition(Args.getLastArg(OPT_N)->getValue(), 214*0fca6ea1SDimitry Andric IMAGE_FILE_MACHINE_ARM64, AddUnderscores, 215*0fca6ea1SDimitry Andric NativeExports, OutputFile)) 2160b57cec5SDimitry Andric return 1; 2170b57cec5SDimitry Andric } 2180b57cec5SDimitry Andric 219*0fca6ea1SDimitry Andric if (!parseModuleDefinition(Args.getLastArg(OPT_d)->getValue(), Machine, 220*0fca6ea1SDimitry Andric AddUnderscores, Exports, OutputFile)) 221*0fca6ea1SDimitry Andric return 1; 2220b57cec5SDimitry Andric 223*0fca6ea1SDimitry Andric if (OutputFile.empty()) { 2248bcb0991SDimitry Andric llvm::errs() << "no DLL name specified\n"; 2250b57cec5SDimitry Andric return 1; 2260b57cec5SDimitry Andric } 2270b57cec5SDimitry Andric 22806c3fb27SDimitry Andric if (Machine == IMAGE_FILE_MACHINE_I386 && Args.hasArg(OPT_k)) { 229*0fca6ea1SDimitry Andric for (COFFShortExport &E : Exports) { 230*0fca6ea1SDimitry Andric if (!E.ImportName.empty() || (!E.Name.empty() && E.Name[0] == '?')) 2310b57cec5SDimitry Andric continue; 2320b57cec5SDimitry Andric E.SymbolName = E.Name; 2330b57cec5SDimitry Andric // Trim off the trailing decoration. Symbols will always have a 2340b57cec5SDimitry Andric // starting prefix here (either _ for cdecl/stdcall, @ for fastcall 2350b57cec5SDimitry Andric // or ? for C++ functions). Vectorcall functions won't have any 2360b57cec5SDimitry Andric // fixed prefix, but the function base name will still be at least 2370b57cec5SDimitry Andric // one char. 2380b57cec5SDimitry Andric E.Name = E.Name.substr(0, E.Name.find('@', 1)); 2390b57cec5SDimitry Andric // By making sure E.SymbolName != E.Name for decorated symbols, 2400b57cec5SDimitry Andric // writeImportLibrary writes these symbols with the type 2410b57cec5SDimitry Andric // IMPORT_NAME_UNDECORATE. 2420b57cec5SDimitry Andric } 2430b57cec5SDimitry Andric } 2440b57cec5SDimitry Andric 245*0fca6ea1SDimitry Andric std::string Path = std::string(Args.getLastArgValue(OPT_l)); 246*0fca6ea1SDimitry Andric if (!Path.empty() && writeImportLibrary(OutputFile, Path, Exports, Machine, 247*0fca6ea1SDimitry Andric /*MinGW=*/true, NativeExports)) 2480b57cec5SDimitry Andric return 1; 2490b57cec5SDimitry Andric return 0; 2500b57cec5SDimitry Andric } 251