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/Optional.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/Option.h" 21 #include "llvm/Support/Host.h" 22 #include "llvm/Support/Path.h" 23 24 #include <vector> 25 26 using namespace llvm; 27 using namespace llvm::object; 28 using namespace llvm::COFF; 29 30 namespace { 31 32 enum { 33 OPT_INVALID = 0, 34 #define OPTION(_1, _2, ID, _4, _5, _6, _7, _8, _9, _10, _11, _12) OPT_##ID, 35 #include "Options.inc" 36 #undef OPTION 37 }; 38 39 #define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; 40 #include "Options.inc" 41 #undef PREFIX 42 43 static const llvm::opt::OptTable::Info InfoTable[] = { 44 #define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X7, X8, X9, X10, X11, X12) \ 45 {X1, X2, X10, X11, OPT_##ID, llvm::opt::Option::KIND##Class, \ 46 X9, X8, OPT_##GROUP, OPT_##ALIAS, X7, X12}, 47 #include "Options.inc" 48 #undef OPTION 49 }; 50 51 class DllOptTable : public llvm::opt::OptTable { 52 public: 53 DllOptTable() : OptTable(InfoTable, false) {} 54 }; 55 56 // Opens a file. Path has to be resolved already. 57 std::unique_ptr<MemoryBuffer> openFile(const Twine &Path) { 58 ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> MB = MemoryBuffer::getFile(Path); 59 60 if (std::error_code EC = MB.getError()) { 61 llvm::errs() << "cannot open file " << Path << ": " << EC.message() << "\n"; 62 return nullptr; 63 } 64 65 return std::move(*MB); 66 } 67 68 MachineTypes getEmulation(StringRef S) { 69 return StringSwitch<MachineTypes>(S) 70 .Case("i386", IMAGE_FILE_MACHINE_I386) 71 .Case("i386:x86-64", IMAGE_FILE_MACHINE_AMD64) 72 .Case("arm", IMAGE_FILE_MACHINE_ARMNT) 73 .Case("arm64", IMAGE_FILE_MACHINE_ARM64) 74 .Default(IMAGE_FILE_MACHINE_UNKNOWN); 75 } 76 77 MachineTypes getMachine(Triple T) { 78 switch (T.getArch()) { 79 case Triple::x86: 80 return COFF::IMAGE_FILE_MACHINE_I386; 81 case Triple::x86_64: 82 return COFF::IMAGE_FILE_MACHINE_AMD64; 83 case Triple::arm: 84 return COFF::IMAGE_FILE_MACHINE_ARMNT; 85 case Triple::aarch64: 86 return COFF::IMAGE_FILE_MACHINE_ARM64; 87 default: 88 return COFF::IMAGE_FILE_MACHINE_UNKNOWN; 89 } 90 } 91 92 MachineTypes getDefaultMachine() { 93 return getMachine(Triple(sys::getDefaultTargetTriple())); 94 } 95 96 static bool consume_back_lower(StringRef &S, const char *Str) { 97 if (!S.endswith_lower(Str)) 98 return false; 99 S = S.drop_back(strlen(Str)); 100 return true; 101 } 102 103 Optional<std::string> getPrefix(StringRef Argv0) { 104 StringRef ProgName = llvm::sys::path::stem(Argv0); 105 // x86_64-w64-mingw32-dlltool -> x86_64-w64-mingw32 106 // llvm-dlltool -> None 107 // aarch64-w64-mingw32-llvm-dlltool-10.exe -> aarch64-w64-mingw32 108 ProgName = ProgName.rtrim("0123456789.-"); 109 if (!consume_back_lower(ProgName, "dlltool")) 110 return None; 111 consume_back_lower(ProgName, "llvm-"); 112 consume_back_lower(ProgName, "-"); 113 return ProgName.str(); 114 } 115 116 } // namespace 117 118 int llvm::dlltoolDriverMain(llvm::ArrayRef<const char *> ArgsArr) { 119 DllOptTable Table; 120 unsigned MissingIndex; 121 unsigned MissingCount; 122 llvm::opt::InputArgList Args = 123 Table.ParseArgs(ArgsArr.slice(1), MissingIndex, MissingCount); 124 if (MissingCount) { 125 llvm::errs() << Args.getArgString(MissingIndex) << ": missing argument\n"; 126 return 1; 127 } 128 129 // Handle when no input or output is specified 130 if (Args.hasArgNoClaim(OPT_INPUT) || 131 (!Args.hasArgNoClaim(OPT_d) && !Args.hasArgNoClaim(OPT_l))) { 132 Table.PrintHelp(outs(), "llvm-dlltool [options] file...", "llvm-dlltool", 133 false); 134 llvm::outs() << "\nTARGETS: i386, i386:x86-64, arm, arm64\n"; 135 return 1; 136 } 137 138 for (auto *Arg : Args.filtered(OPT_UNKNOWN)) 139 llvm::errs() << "ignoring unknown argument: " << Arg->getAsString(Args) 140 << "\n"; 141 142 if (!Args.hasArg(OPT_d)) { 143 llvm::errs() << "no definition file specified\n"; 144 return 1; 145 } 146 147 std::unique_ptr<MemoryBuffer> MB = 148 openFile(Args.getLastArg(OPT_d)->getValue()); 149 if (!MB) 150 return 1; 151 152 if (!MB->getBufferSize()) { 153 llvm::errs() << "definition file empty\n"; 154 return 1; 155 } 156 157 COFF::MachineTypes Machine = getDefaultMachine(); 158 if (Optional<std::string> Prefix = getPrefix(ArgsArr[0])) { 159 Triple T(*Prefix); 160 if (T.getArch() != Triple::UnknownArch) 161 Machine = getMachine(T); 162 } 163 if (auto *Arg = Args.getLastArg(OPT_m)) 164 Machine = getEmulation(Arg->getValue()); 165 166 if (Machine == IMAGE_FILE_MACHINE_UNKNOWN) { 167 llvm::errs() << "unknown target\n"; 168 return 1; 169 } 170 171 Expected<COFFModuleDefinition> Def = 172 parseCOFFModuleDefinition(*MB, Machine, true); 173 174 if (!Def) { 175 llvm::errs() << "error parsing definition\n" 176 << errorToErrorCode(Def.takeError()).message(); 177 return 1; 178 } 179 180 // Do this after the parser because parseCOFFModuleDefinition sets OutputFile. 181 if (auto *Arg = Args.getLastArg(OPT_D)) 182 Def->OutputFile = Arg->getValue(); 183 184 if (Def->OutputFile.empty()) { 185 llvm::errs() << "no DLL name specified\n"; 186 return 1; 187 } 188 189 std::string Path = std::string(Args.getLastArgValue(OPT_l)); 190 191 // If ExtName is set (if the "ExtName = Name" syntax was used), overwrite 192 // Name with ExtName and clear ExtName. When only creating an import 193 // library and not linking, the internal name is irrelevant. This avoids 194 // cases where writeImportLibrary tries to transplant decoration from 195 // symbol decoration onto ExtName. 196 for (COFFShortExport& E : Def->Exports) { 197 if (!E.ExtName.empty()) { 198 E.Name = E.ExtName; 199 E.ExtName.clear(); 200 } 201 } 202 203 if (Machine == IMAGE_FILE_MACHINE_I386 && Args.getLastArg(OPT_k)) { 204 for (COFFShortExport& E : Def->Exports) { 205 if (!E.AliasTarget.empty() || (!E.Name.empty() && E.Name[0] == '?')) 206 continue; 207 E.SymbolName = E.Name; 208 // Trim off the trailing decoration. Symbols will always have a 209 // starting prefix here (either _ for cdecl/stdcall, @ for fastcall 210 // or ? for C++ functions). Vectorcall functions won't have any 211 // fixed prefix, but the function base name will still be at least 212 // one char. 213 E.Name = E.Name.substr(0, E.Name.find('@', 1)); 214 // By making sure E.SymbolName != E.Name for decorated symbols, 215 // writeImportLibrary writes these symbols with the type 216 // IMPORT_NAME_UNDECORATE. 217 } 218 } 219 220 if (!Path.empty() && 221 writeImportLibrary(Def->OutputFile, Path, Def->Exports, Machine, true)) 222 return 1; 223 return 0; 224 } 225