1 //===- DlltoolDriver.cpp - dlltool.exe-compatible driver ------------------===//
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
7 //===----------------------------------------------------------------------===//
9 // Defines an interface to a dlltool.exe-compatible driver.
11 //===----------------------------------------------------------------------===//
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"
29 using namespace llvm::object
;
30 using namespace llvm::COFF
;
34 #define OPTTABLE_STR_TABLE_CODE
35 #include "Options.inc"
36 #undef OPTTABLE_STR_TABLE_CODE
40 #define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),
41 #include "Options.inc"
45 #define OPTTABLE_PREFIXES_TABLE_CODE
46 #include "Options.inc"
47 #undef OPTTABLE_PREFIXES_TABLE_CODE
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"
56 class DllOptTable
: public opt::GenericOptTable
{
59 : opt::GenericOptTable(OptionStrTable
, OptionPrefixesTable
, InfoTable
,
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
);
67 if (std::error_code EC
= MB
.getError()) {
68 llvm::errs() << "cannot open file " << Path
<< ": " << EC
.message() << "\n";
72 return std::move(*MB
);
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
);
86 MachineTypes
getMachine(Triple T
) {
87 switch (T
.getArch()) {
89 return COFF::IMAGE_FILE_MACHINE_I386
;
91 return COFF::IMAGE_FILE_MACHINE_AMD64
;
93 return COFF::IMAGE_FILE_MACHINE_ARMNT
;
95 return T
.isWindowsArm64EC() ? COFF::IMAGE_FILE_MACHINE_ARM64EC
96 : COFF::IMAGE_FILE_MACHINE_ARM64
;
98 return COFF::IMAGE_FILE_MACHINE_R4000
;
100 return COFF::IMAGE_FILE_MACHINE_UNKNOWN
;
104 MachineTypes
getDefaultMachine() {
105 return getMachine(Triple(sys::getDefaultTargetTriple()));
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"))
116 ProgName
.consume_back_insensitive("llvm-");
117 ProgName
.consume_back_insensitive("-");
118 return ProgName
.str();
121 bool parseModuleDefinition(StringRef DefFileName
, MachineTypes Machine
,
123 std::vector
<COFFShortExport
> &Exports
,
124 std::string
&OutputFile
) {
125 std::unique_ptr
<MemoryBuffer
> MB
= openFile(DefFileName
);
129 if (!MB
->getBufferSize()) {
130 llvm::errs() << "definition file empty\n";
134 Expected
<COFFModuleDefinition
> Def
= parseCOFFModuleDefinition(
135 *MB
, Machine
, /*MingwDef=*/true, AddUnderscores
);
137 llvm::errs() << "error parsing definition\n"
138 << errorToErrorCode(Def
.takeError()).message() << "\n";
142 if (OutputFile
.empty())
143 OutputFile
= std::move(Def
->OutputFile
);
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()) {
157 Exports
= std::move(Def
->Exports
);
163 int llvm::dlltoolDriverMain(llvm::ArrayRef
<const char *> ArgsArr
) {
165 unsigned MissingIndex
;
166 unsigned MissingCount
;
167 llvm::opt::InputArgList Args
=
168 Table
.ParseArgs(ArgsArr
.slice(1), MissingIndex
, MissingCount
);
170 llvm::errs() << Args
.getArgString(MissingIndex
) << ": missing argument\n";
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",
180 << "\nTARGETS: i386, i386:x86-64, arm, arm64, arm64ec, r4000\n";
184 for (auto *Arg
: Args
.filtered(OPT_UNKNOWN
))
185 llvm::errs() << "ignoring unknown argument: " << Arg
->getAsString(Args
)
188 if (!Args
.hasArg(OPT_d
)) {
189 llvm::errs() << "no definition file specified\n";
193 COFF::MachineTypes Machine
= getDefaultMachine();
194 if (std::optional
<std::string
> Prefix
= getPrefix(ArgsArr
[0])) {
196 if (T
.getArch() != Triple::UnknownArch
)
197 Machine
= getMachine(T
);
199 if (auto *Arg
= Args
.getLastArg(OPT_m
))
200 Machine
= getEmulation(Arg
->getValue());
202 if (Machine
== IMAGE_FILE_MACHINE_UNKNOWN
) {
203 llvm::errs() << "unknown target\n";
207 bool AddUnderscores
= !Args
.hasArg(OPT_no_leading_underscore
);
209 std::string OutputFile
;
210 if (auto *Arg
= Args
.getLastArg(OPT_D
))
211 OutputFile
= Arg
->getValue();
213 std::vector
<COFFShortExport
> Exports
, NativeExports
;
215 if (Args
.hasArg(OPT_N
)) {
216 if (!isArm64EC(Machine
)) {
217 llvm::errs() << "native .def file is supported only on arm64ec target\n";
220 if (!parseModuleDefinition(Args
.getLastArg(OPT_N
)->getValue(),
221 IMAGE_FILE_MACHINE_ARM64
, AddUnderscores
,
222 NativeExports
, OutputFile
))
226 if (!parseModuleDefinition(Args
.getLastArg(OPT_d
)->getValue(), Machine
,
227 AddUnderscores
, Exports
, OutputFile
))
230 if (OutputFile
.empty()) {
231 llvm::errs() << "no DLL name specified\n";
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] == '?'))
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
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.
252 std::string Path
= std::string(Args
.getLastArgValue(OPT_l
));
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";