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