1 //===- DlltoolDriver.cpp - dlltool.exe-compatible driver ------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // Defines an interface to a dlltool.exe-compatible driver. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "llvm/ToolDrivers/llvm-dlltool/DlltoolDriver.h" 14 #include "llvm/ADT/StringSwitch.h" 15 #include "llvm/Object/COFF.h" 16 #include "llvm/Object/COFFImportFile.h" 17 #include "llvm/Object/COFFModuleDefinition.h" 18 #include "llvm/Option/Arg.h" 19 #include "llvm/Option/ArgList.h" 20 #include "llvm/Option/OptTable.h" 21 #include "llvm/Option/Option.h" 22 #include "llvm/Support/Path.h" 23 #include "llvm/TargetParser/Host.h" 24 25 #include <optional> 26 #include <vector> 27 28 using namespace llvm; 29 using namespace llvm::object; 30 using namespace llvm::COFF; 31 32 namespace { 33 34 #define OPTTABLE_STR_TABLE_CODE 35 #include "Options.inc" 36 #undef OPTTABLE_STR_TABLE_CODE 37 38 enum { 39 OPT_INVALID = 0, 40 #define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__), 41 #include "Options.inc" 42 #undef OPTION 43 }; 44 45 #define OPTTABLE_PREFIXES_TABLE_CODE 46 #include "Options.inc" 47 #undef OPTTABLE_PREFIXES_TABLE_CODE 48 49 using namespace llvm::opt; 50 static constexpr opt::OptTable::Info InfoTable[] = { 51 #define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__), 52 #include "Options.inc" 53 #undef OPTION 54 }; 55 56 class DllOptTable : public opt::GenericOptTable { 57 public: 58 DllOptTable() 59 : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable, 60 false) {} 61 }; 62 63 // Opens a file. Path has to be resolved already. 64 std::unique_ptr<MemoryBuffer> openFile(const Twine &Path) { 65 ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> MB = MemoryBuffer::getFile(Path); 66 67 if (std::error_code EC = MB.getError()) { 68 llvm::errs() << "cannot open file " << Path << ": " << EC.message() << "\n"; 69 return nullptr; 70 } 71 72 return std::move(*MB); 73 } 74 75 MachineTypes getEmulation(StringRef S) { 76 return StringSwitch<MachineTypes>(S) 77 .Case("i386", IMAGE_FILE_MACHINE_I386) 78 .Case("i386:x86-64", IMAGE_FILE_MACHINE_AMD64) 79 .Case("arm", IMAGE_FILE_MACHINE_ARMNT) 80 .Case("arm64", IMAGE_FILE_MACHINE_ARM64) 81 .Case("arm64ec", IMAGE_FILE_MACHINE_ARM64EC) 82 .Case("r4000", IMAGE_FILE_MACHINE_R4000) 83 .Default(IMAGE_FILE_MACHINE_UNKNOWN); 84 } 85 86 MachineTypes getMachine(Triple T) { 87 switch (T.getArch()) { 88 case Triple::x86: 89 return COFF::IMAGE_FILE_MACHINE_I386; 90 case Triple::x86_64: 91 return COFF::IMAGE_FILE_MACHINE_AMD64; 92 case Triple::arm: 93 return COFF::IMAGE_FILE_MACHINE_ARMNT; 94 case Triple::aarch64: 95 return T.isWindowsArm64EC() ? COFF::IMAGE_FILE_MACHINE_ARM64EC 96 : COFF::IMAGE_FILE_MACHINE_ARM64; 97 case Triple::mipsel: 98 return COFF::IMAGE_FILE_MACHINE_R4000; 99 default: 100 return COFF::IMAGE_FILE_MACHINE_UNKNOWN; 101 } 102 } 103 104 MachineTypes getDefaultMachine() { 105 return getMachine(Triple(sys::getDefaultTargetTriple())); 106 } 107 108 std::optional<std::string> getPrefix(StringRef Argv0) { 109 StringRef ProgName = llvm::sys::path::stem(Argv0); 110 // x86_64-w64-mingw32-dlltool -> x86_64-w64-mingw32 111 // llvm-dlltool -> None 112 // aarch64-w64-mingw32-llvm-dlltool-10.exe -> aarch64-w64-mingw32 113 ProgName = ProgName.rtrim("0123456789.-"); 114 if (!ProgName.consume_back_insensitive("dlltool")) 115 return std::nullopt; 116 ProgName.consume_back_insensitive("llvm-"); 117 ProgName.consume_back_insensitive("-"); 118 return ProgName.str(); 119 } 120 121 bool parseModuleDefinition(StringRef DefFileName, MachineTypes Machine, 122 bool AddUnderscores, 123 std::vector<COFFShortExport> &Exports, 124 std::string &OutputFile) { 125 std::unique_ptr<MemoryBuffer> MB = openFile(DefFileName); 126 if (!MB) 127 return false; 128 129 if (!MB->getBufferSize()) { 130 llvm::errs() << "definition file empty\n"; 131 return false; 132 } 133 134 Expected<COFFModuleDefinition> Def = parseCOFFModuleDefinition( 135 *MB, Machine, /*MingwDef=*/true, AddUnderscores); 136 if (!Def) { 137 llvm::errs() << "error parsing definition\n" 138 << errorToErrorCode(Def.takeError()).message() << "\n"; 139 return false; 140 } 141 142 if (OutputFile.empty()) 143 OutputFile = std::move(Def->OutputFile); 144 145 // If ExtName is set (if the "ExtName = Name" syntax was used), overwrite 146 // Name with ExtName and clear ExtName. When only creating an import 147 // library and not linking, the internal name is irrelevant. This avoids 148 // cases where writeImportLibrary tries to transplant decoration from 149 // symbol decoration onto ExtName. 150 for (COFFShortExport &E : Def->Exports) { 151 if (!E.ExtName.empty()) { 152 E.Name = E.ExtName; 153 E.ExtName.clear(); 154 } 155 } 156 157 Exports = std::move(Def->Exports); 158 return true; 159 } 160 161 } // namespace 162 163 int llvm::dlltoolDriverMain(llvm::ArrayRef<const char *> ArgsArr) { 164 DllOptTable Table; 165 unsigned MissingIndex; 166 unsigned MissingCount; 167 llvm::opt::InputArgList Args = 168 Table.ParseArgs(ArgsArr.slice(1), MissingIndex, MissingCount); 169 if (MissingCount) { 170 llvm::errs() << Args.getArgString(MissingIndex) << ": missing argument\n"; 171 return 1; 172 } 173 174 // Handle when no input or output is specified 175 if (Args.hasArgNoClaim(OPT_INPUT) || 176 (!Args.hasArgNoClaim(OPT_d) && !Args.hasArgNoClaim(OPT_l))) { 177 Table.printHelp(outs(), "llvm-dlltool [options] file...", "llvm-dlltool", 178 false); 179 llvm::outs() 180 << "\nTARGETS: i386, i386:x86-64, arm, arm64, arm64ec, r4000\n"; 181 return 1; 182 } 183 184 for (auto *Arg : Args.filtered(OPT_UNKNOWN)) 185 llvm::errs() << "ignoring unknown argument: " << Arg->getAsString(Args) 186 << "\n"; 187 188 if (!Args.hasArg(OPT_d)) { 189 llvm::errs() << "no definition file specified\n"; 190 return 1; 191 } 192 193 COFF::MachineTypes Machine = getDefaultMachine(); 194 if (std::optional<std::string> Prefix = getPrefix(ArgsArr[0])) { 195 Triple T(*Prefix); 196 if (T.getArch() != Triple::UnknownArch) 197 Machine = getMachine(T); 198 } 199 if (auto *Arg = Args.getLastArg(OPT_m)) 200 Machine = getEmulation(Arg->getValue()); 201 202 if (Machine == IMAGE_FILE_MACHINE_UNKNOWN) { 203 llvm::errs() << "unknown target\n"; 204 return 1; 205 } 206 207 bool AddUnderscores = !Args.hasArg(OPT_no_leading_underscore); 208 209 std::string OutputFile; 210 if (auto *Arg = Args.getLastArg(OPT_D)) 211 OutputFile = Arg->getValue(); 212 213 std::vector<COFFShortExport> Exports, NativeExports; 214 215 if (Args.hasArg(OPT_N)) { 216 if (!isArm64EC(Machine)) { 217 llvm::errs() << "native .def file is supported only on arm64ec target\n"; 218 return 1; 219 } 220 if (!parseModuleDefinition(Args.getLastArg(OPT_N)->getValue(), 221 IMAGE_FILE_MACHINE_ARM64, AddUnderscores, 222 NativeExports, OutputFile)) 223 return 1; 224 } 225 226 if (!parseModuleDefinition(Args.getLastArg(OPT_d)->getValue(), Machine, 227 AddUnderscores, Exports, OutputFile)) 228 return 1; 229 230 if (OutputFile.empty()) { 231 llvm::errs() << "no DLL name specified\n"; 232 return 1; 233 } 234 235 if (Machine == IMAGE_FILE_MACHINE_I386 && Args.hasArg(OPT_k)) { 236 for (COFFShortExport &E : Exports) { 237 if (!E.ImportName.empty() || (!E.Name.empty() && E.Name[0] == '?')) 238 continue; 239 E.SymbolName = E.Name; 240 // Trim off the trailing decoration. Symbols will always have a 241 // starting prefix here (either _ for cdecl/stdcall, @ for fastcall 242 // or ? for C++ functions). Vectorcall functions won't have any 243 // fixed prefix, but the function base name will still be at least 244 // one char. 245 E.Name = E.Name.substr(0, E.Name.find('@', 1)); 246 // By making sure E.SymbolName != E.Name for decorated symbols, 247 // writeImportLibrary writes these symbols with the type 248 // IMPORT_NAME_UNDECORATE. 249 } 250 } 251 252 std::string Path = std::string(Args.getLastArgValue(OPT_l)); 253 if (!Path.empty()) { 254 if (Error E = writeImportLibrary(OutputFile, Path, Exports, Machine, 255 /*MinGW=*/true, NativeExports)) { 256 handleAllErrors(std::move(E), [&](const ErrorInfoBase &EI) { 257 llvm::errs() << EI.message() << "\n"; 258 }); 259 return 1; 260 } 261 } 262 return 0; 263 } 264