Path: blob/main/contrib/llvm-project/llvm/lib/ToolDrivers/llvm-dlltool/DlltoolDriver.cpp
35271 views
//===- DlltoolDriver.cpp - dlltool.exe-compatible driver ------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// Defines an interface to a dlltool.exe-compatible driver.9//10//===----------------------------------------------------------------------===//1112#include "llvm/ToolDrivers/llvm-dlltool/DlltoolDriver.h"13#include "llvm/ADT/StringSwitch.h"14#include "llvm/Object/COFF.h"15#include "llvm/Object/COFFImportFile.h"16#include "llvm/Object/COFFModuleDefinition.h"17#include "llvm/Option/Arg.h"18#include "llvm/Option/ArgList.h"19#include "llvm/Option/OptTable.h"20#include "llvm/Option/Option.h"21#include "llvm/Support/Path.h"22#include "llvm/TargetParser/Host.h"2324#include <optional>25#include <vector>2627using namespace llvm;28using namespace llvm::object;29using namespace llvm::COFF;3031namespace {3233enum {34OPT_INVALID = 0,35#define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),36#include "Options.inc"37#undef OPTION38};3940#define PREFIX(NAME, VALUE) \41static constexpr StringLiteral NAME##_init[] = VALUE; \42static constexpr ArrayRef<StringLiteral> NAME(NAME##_init, \43std::size(NAME##_init) - 1);44#include "Options.inc"45#undef PREFIX4647using namespace llvm::opt;48static constexpr opt::OptTable::Info InfoTable[] = {49#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),50#include "Options.inc"51#undef OPTION52};5354class DllOptTable : public opt::GenericOptTable {55public:56DllOptTable() : opt::GenericOptTable(InfoTable, false) {}57};5859// Opens a file. Path has to be resolved already.60std::unique_ptr<MemoryBuffer> openFile(const Twine &Path) {61ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> MB = MemoryBuffer::getFile(Path);6263if (std::error_code EC = MB.getError()) {64llvm::errs() << "cannot open file " << Path << ": " << EC.message() << "\n";65return nullptr;66}6768return std::move(*MB);69}7071MachineTypes getEmulation(StringRef S) {72return StringSwitch<MachineTypes>(S)73.Case("i386", IMAGE_FILE_MACHINE_I386)74.Case("i386:x86-64", IMAGE_FILE_MACHINE_AMD64)75.Case("arm", IMAGE_FILE_MACHINE_ARMNT)76.Case("arm64", IMAGE_FILE_MACHINE_ARM64)77.Case("arm64ec", IMAGE_FILE_MACHINE_ARM64EC)78.Default(IMAGE_FILE_MACHINE_UNKNOWN);79}8081MachineTypes getMachine(Triple T) {82switch (T.getArch()) {83case Triple::x86:84return COFF::IMAGE_FILE_MACHINE_I386;85case Triple::x86_64:86return COFF::IMAGE_FILE_MACHINE_AMD64;87case Triple::arm:88return COFF::IMAGE_FILE_MACHINE_ARMNT;89case Triple::aarch64:90return T.isWindowsArm64EC() ? COFF::IMAGE_FILE_MACHINE_ARM64EC91: COFF::IMAGE_FILE_MACHINE_ARM64;92default:93return COFF::IMAGE_FILE_MACHINE_UNKNOWN;94}95}9697MachineTypes getDefaultMachine() {98return getMachine(Triple(sys::getDefaultTargetTriple()));99}100101std::optional<std::string> getPrefix(StringRef Argv0) {102StringRef ProgName = llvm::sys::path::stem(Argv0);103// x86_64-w64-mingw32-dlltool -> x86_64-w64-mingw32104// llvm-dlltool -> None105// aarch64-w64-mingw32-llvm-dlltool-10.exe -> aarch64-w64-mingw32106ProgName = ProgName.rtrim("0123456789.-");107if (!ProgName.consume_back_insensitive("dlltool"))108return std::nullopt;109ProgName.consume_back_insensitive("llvm-");110ProgName.consume_back_insensitive("-");111return ProgName.str();112}113114bool parseModuleDefinition(StringRef DefFileName, MachineTypes Machine,115bool AddUnderscores,116std::vector<COFFShortExport> &Exports,117std::string &OutputFile) {118std::unique_ptr<MemoryBuffer> MB = openFile(DefFileName);119if (!MB)120return false;121122if (!MB->getBufferSize()) {123llvm::errs() << "definition file empty\n";124return false;125}126127Expected<COFFModuleDefinition> Def = parseCOFFModuleDefinition(128*MB, Machine, /*MingwDef=*/true, AddUnderscores);129if (!Def) {130llvm::errs() << "error parsing definition\n"131<< errorToErrorCode(Def.takeError()).message() << "\n";132return false;133}134135if (OutputFile.empty())136OutputFile = std::move(Def->OutputFile);137138// If ExtName is set (if the "ExtName = Name" syntax was used), overwrite139// Name with ExtName and clear ExtName. When only creating an import140// library and not linking, the internal name is irrelevant. This avoids141// cases where writeImportLibrary tries to transplant decoration from142// symbol decoration onto ExtName.143for (COFFShortExport &E : Def->Exports) {144if (!E.ExtName.empty()) {145E.Name = E.ExtName;146E.ExtName.clear();147}148}149150Exports = std::move(Def->Exports);151return true;152}153154} // namespace155156int llvm::dlltoolDriverMain(llvm::ArrayRef<const char *> ArgsArr) {157DllOptTable Table;158unsigned MissingIndex;159unsigned MissingCount;160llvm::opt::InputArgList Args =161Table.ParseArgs(ArgsArr.slice(1), MissingIndex, MissingCount);162if (MissingCount) {163llvm::errs() << Args.getArgString(MissingIndex) << ": missing argument\n";164return 1;165}166167// Handle when no input or output is specified168if (Args.hasArgNoClaim(OPT_INPUT) ||169(!Args.hasArgNoClaim(OPT_d) && !Args.hasArgNoClaim(OPT_l))) {170Table.printHelp(outs(), "llvm-dlltool [options] file...", "llvm-dlltool",171false);172llvm::outs() << "\nTARGETS: i386, i386:x86-64, arm, arm64, arm64ec\n";173return 1;174}175176for (auto *Arg : Args.filtered(OPT_UNKNOWN))177llvm::errs() << "ignoring unknown argument: " << Arg->getAsString(Args)178<< "\n";179180if (!Args.hasArg(OPT_d)) {181llvm::errs() << "no definition file specified\n";182return 1;183}184185COFF::MachineTypes Machine = getDefaultMachine();186if (std::optional<std::string> Prefix = getPrefix(ArgsArr[0])) {187Triple T(*Prefix);188if (T.getArch() != Triple::UnknownArch)189Machine = getMachine(T);190}191if (auto *Arg = Args.getLastArg(OPT_m))192Machine = getEmulation(Arg->getValue());193194if (Machine == IMAGE_FILE_MACHINE_UNKNOWN) {195llvm::errs() << "unknown target\n";196return 1;197}198199bool AddUnderscores = !Args.hasArg(OPT_no_leading_underscore);200201std::string OutputFile;202if (auto *Arg = Args.getLastArg(OPT_D))203OutputFile = Arg->getValue();204205std::vector<COFFShortExport> Exports, NativeExports;206207if (Args.hasArg(OPT_N)) {208if (!isArm64EC(Machine)) {209llvm::errs() << "native .def file is supported only on arm64ec target\n";210return 1;211}212if (!parseModuleDefinition(Args.getLastArg(OPT_N)->getValue(),213IMAGE_FILE_MACHINE_ARM64, AddUnderscores,214NativeExports, OutputFile))215return 1;216}217218if (!parseModuleDefinition(Args.getLastArg(OPT_d)->getValue(), Machine,219AddUnderscores, Exports, OutputFile))220return 1;221222if (OutputFile.empty()) {223llvm::errs() << "no DLL name specified\n";224return 1;225}226227if (Machine == IMAGE_FILE_MACHINE_I386 && Args.hasArg(OPT_k)) {228for (COFFShortExport &E : Exports) {229if (!E.ImportName.empty() || (!E.Name.empty() && E.Name[0] == '?'))230continue;231E.SymbolName = E.Name;232// Trim off the trailing decoration. Symbols will always have a233// starting prefix here (either _ for cdecl/stdcall, @ for fastcall234// or ? for C++ functions). Vectorcall functions won't have any235// fixed prefix, but the function base name will still be at least236// one char.237E.Name = E.Name.substr(0, E.Name.find('@', 1));238// By making sure E.SymbolName != E.Name for decorated symbols,239// writeImportLibrary writes these symbols with the type240// IMPORT_NAME_UNDECORATE.241}242}243244std::string Path = std::string(Args.getLastArgValue(OPT_l));245if (!Path.empty() && writeImportLibrary(OutputFile, Path, Exports, Machine,246/*MinGW=*/true, NativeExports))247return 1;248return 0;249}250251252