brintos

brintos / llvm-project-archived public Read only

0
0
Text · 13.2 KiB · 380fbd8 Raw
408 lines · cpp
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-exception6//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/Archive.h"16#include "llvm/Object/COFF.h"17#include "llvm/Object/COFFImportFile.h"18#include "llvm/Object/COFFModuleDefinition.h"19#include "llvm/Option/Arg.h"20#include "llvm/Option/ArgList.h"21#include "llvm/Option/OptTable.h"22#include "llvm/Option/Option.h"23#include "llvm/Support/Path.h"24#include "llvm/TargetParser/Host.h"25 26#include <optional>27#include <vector>28 29using namespace llvm;30using namespace llvm::object;31using namespace llvm::COFF;32 33namespace {34 35#define OPTTABLE_STR_TABLE_CODE36#include "Options.inc"37#undef OPTTABLE_STR_TABLE_CODE38 39enum {40  OPT_INVALID = 0,41#define OPTION(...) LLVM_MAKE_OPT_ID(__VA_ARGS__),42#include "Options.inc"43#undef OPTION44};45 46#define OPTTABLE_PREFIXES_TABLE_CODE47#include "Options.inc"48#undef OPTTABLE_PREFIXES_TABLE_CODE49 50using namespace llvm::opt;51static constexpr opt::OptTable::Info InfoTable[] = {52#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),53#include "Options.inc"54#undef OPTION55};56 57class DllOptTable : public opt::GenericOptTable {58public:59  DllOptTable()60      : opt::GenericOptTable(OptionStrTable, OptionPrefixesTable, InfoTable,61                             false) {}62};63 64// Opens a file. Path has to be resolved already.65std::unique_ptr<MemoryBuffer> openFile(const Twine &Path) {66  ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> MB = MemoryBuffer::getFile(Path);67 68  if (std::error_code EC = MB.getError()) {69    llvm::errs() << "cannot open file " << Path << ": " << EC.message() << "\n";70    return nullptr;71  }72 73  return std::move(*MB);74}75 76MachineTypes getEmulation(StringRef S) {77  return StringSwitch<MachineTypes>(S)78      .Case("i386", IMAGE_FILE_MACHINE_I386)79      .Case("i386:x86-64", IMAGE_FILE_MACHINE_AMD64)80      .Case("arm", IMAGE_FILE_MACHINE_ARMNT)81      .Case("arm64", IMAGE_FILE_MACHINE_ARM64)82      .Case("arm64ec", IMAGE_FILE_MACHINE_ARM64EC)83      .Case("r4000", IMAGE_FILE_MACHINE_R4000)84      .Default(IMAGE_FILE_MACHINE_UNKNOWN);85}86 87MachineTypes getMachine(Triple T) {88  switch (T.getArch()) {89  case Triple::x86:90    return COFF::IMAGE_FILE_MACHINE_I386;91  case Triple::x86_64:92    return COFF::IMAGE_FILE_MACHINE_AMD64;93  case Triple::arm:94    return COFF::IMAGE_FILE_MACHINE_ARMNT;95  case Triple::aarch64:96    return T.isWindowsArm64EC() ? COFF::IMAGE_FILE_MACHINE_ARM64EC97                                : COFF::IMAGE_FILE_MACHINE_ARM64;98  case Triple::mipsel:99    return COFF::IMAGE_FILE_MACHINE_R4000;100  default:101    return COFF::IMAGE_FILE_MACHINE_UNKNOWN;102  }103}104 105MachineTypes getDefaultMachine() {106  return getMachine(Triple(sys::getDefaultTargetTriple()));107}108 109std::optional<std::string> getPrefix(StringRef Argv0) {110  StringRef ProgName = llvm::sys::path::stem(Argv0);111  // x86_64-w64-mingw32-dlltool -> x86_64-w64-mingw32112  // llvm-dlltool -> None113  // aarch64-w64-mingw32-llvm-dlltool-10.exe -> aarch64-w64-mingw32114  ProgName = ProgName.rtrim("0123456789.-");115  if (!ProgName.consume_back_insensitive("dlltool"))116    return std::nullopt;117  ProgName.consume_back_insensitive("llvm-");118  ProgName.consume_back_insensitive("-");119  return ProgName.str();120}121 122bool parseModuleDefinition(StringRef DefFileName, MachineTypes Machine,123                           bool AddUnderscores,124                           std::vector<COFFShortExport> &Exports,125                           std::string &OutputFile) {126  std::unique_ptr<MemoryBuffer> MB = openFile(DefFileName);127  if (!MB)128    return false;129 130  if (!MB->getBufferSize()) {131    llvm::errs() << "definition file empty\n";132    return false;133  }134 135  Expected<COFFModuleDefinition> Def = parseCOFFModuleDefinition(136      *MB, Machine, /*MingwDef=*/true, AddUnderscores);137  if (!Def) {138    llvm::errs() << "error parsing definition\n"139                 << errorToErrorCode(Def.takeError()).message() << "\n";140    return false;141  }142 143  if (OutputFile.empty())144    OutputFile = std::move(Def->OutputFile);145 146  // If ExtName is set (if the "ExtName = Name" syntax was used), overwrite147  // Name with ExtName and clear ExtName. When only creating an import148  // library and not linking, the internal name is irrelevant. This avoids149  // cases where writeImportLibrary tries to transplant decoration from150  // symbol decoration onto ExtName.151  for (COFFShortExport &E : Def->Exports) {152    if (!E.ExtName.empty()) {153      E.Name = E.ExtName;154      E.ExtName.clear();155    }156  }157 158  Exports = std::move(Def->Exports);159  return true;160}161 162int printError(llvm::Error E, Twine File) {163  if (!E)164    return 0;165  handleAllErrors(std::move(E), [&](const llvm::ErrorInfoBase &EIB) {166    llvm::errs() << "error opening " << File << ": " << EIB.message() << "\n";167  });168  return 1;169}170 171template <typename Callable>172int forEachCoff(object::Archive &Archive, StringRef Name, Callable Callback) {173  Error Err = Error::success();174  for (auto &C : Archive.children(Err)) {175    Expected<StringRef> NameOrErr = C.getName();176    if (!NameOrErr)177      return printError(NameOrErr.takeError(), Name);178    StringRef Name = *NameOrErr;179 180    Expected<MemoryBufferRef> ChildMB = C.getMemoryBufferRef();181    if (!ChildMB)182      return printError(ChildMB.takeError(), Name);183 184    if (identify_magic(ChildMB->getBuffer()) == file_magic::coff_object) {185      auto Obj = object::COFFObjectFile::create(*ChildMB);186      if (!Obj)187        return printError(Obj.takeError(), Name);188      if (!Callback(*Obj->get(), Name))189        return 1;190    }191  }192  if (Err)193    return printError(std::move(Err), Name);194  return 0;195}196 197// To find the named of the imported DLL from an import library, we can either198// inspect the object files that form the import table entries, or we could199// just look at the archive member names, for MSVC style import libraries.200// Looking at the archive member names doesn't work for GNU style import201// libraries though, while inspecting the import table entries works for202// both. (MSVC style import libraries contain a couple regular object files203// for the header/trailers.)204//205// This implementation does the same as GNU dlltool does; look at the206// content of ".idata$7" sections, or for MSVC style libraries, look207// at ".idata$6" sections.208//209// For GNU style import libraries, there are also other data chunks in sections210// named ".idata$7" (entries to the IAT or ILT); these are distinguished211// by seeing that they contain relocations. (They also look like an empty212// string when looking for null termination.)213//214// Alternatively, we could do things differently - look for any .idata$2215// section; this would be import directory entries. At offset 0xc in them216// there is the RVA of the import DLL name; look for a relocation at this217// spot and locate the symbol that it points at. That symbol may either218// be within the same object file (in the case of MSVC style import libraries)219// or another object file (in the case of GNU import libraries).220bool identifyImportName(const COFFObjectFile &Obj, StringRef ObjName,221                        std::vector<StringRef> &Names, bool IsMsStyleImplib) {222  StringRef TargetName = IsMsStyleImplib ? ".idata$6" : ".idata$7";223  for (const auto &S : Obj.sections()) {224    Expected<StringRef> NameOrErr = S.getName();225    if (!NameOrErr) {226      printError(NameOrErr.takeError(), ObjName);227      return false;228    }229    StringRef Name = *NameOrErr;230    if (Name != TargetName)231      continue;232 233    // GNU import libraries contain .idata$7 section in the per function234    // objects too, but they contain relocations.235    if (!IsMsStyleImplib && !S.relocations().empty())236      continue;237 238    Expected<StringRef> ContentsOrErr = S.getContents();239    if (!ContentsOrErr) {240      printError(ContentsOrErr.takeError(), ObjName);241      return false;242    }243    StringRef Contents = *ContentsOrErr;244    Contents = Contents.substr(0, Contents.find('\0'));245    if (Contents.empty())246      continue;247    Names.push_back(Contents);248    return true;249  }250  return true;251}252 253int doIdentify(StringRef File, bool IdentifyStrict) {254  ErrorOr<std::unique_ptr<MemoryBuffer>> MaybeBuf = MemoryBuffer::getFile(255      File, /*IsText=*/false, /*RequiredNullTerminator=*/false);256  if (!MaybeBuf)257    return printError(errorCodeToError(MaybeBuf.getError()), File);258  if (identify_magic(MaybeBuf.get()->getBuffer()) != file_magic::archive) {259    llvm::errs() << File << " is not a library\n";260    return 1;261  }262 263  std::unique_ptr<MemoryBuffer> B = std::move(MaybeBuf.get());264  Error Err = Error::success();265  object::Archive Archive(B->getMemBufferRef(), Err);266  if (Err)267    return printError(std::move(Err), B->getBufferIdentifier());268 269  bool IsMsStyleImplib = false;270  for (const auto &S : Archive.symbols()) {271    if (S.getName() == "__NULL_IMPORT_DESCRIPTOR") {272      IsMsStyleImplib = true;273      break;274    }275  }276  std::vector<StringRef> Names;277  if (forEachCoff(Archive, B->getBufferIdentifier(),278                  [&](const COFFObjectFile &Obj, StringRef ObjName) -> bool {279                    return identifyImportName(Obj, ObjName, Names,280                                              IsMsStyleImplib);281                  }))282    return 1;283 284  if (Names.empty()) {285    llvm::errs() << "No DLL import name found in " << File << "\n";286    return 1;287  }288  if (Names.size() > 1 && IdentifyStrict) {289    llvm::errs() << File << "contains imports for two or more DLLs\n";290    return 1;291  }292 293  for (StringRef S : Names)294    llvm::outs() << S << "\n";295 296  return 0;297}298 299} // namespace300 301int llvm::dlltoolDriverMain(llvm::ArrayRef<const char *> ArgsArr) {302  DllOptTable Table;303  unsigned MissingIndex;304  unsigned MissingCount;305  llvm::opt::InputArgList Args =306      Table.ParseArgs(ArgsArr.slice(1), MissingIndex, MissingCount);307  if (MissingCount) {308    llvm::errs() << Args.getArgString(MissingIndex) << ": missing argument\n";309    return 1;310  }311 312  // Handle when no input or output is specified313  if (Args.hasArgNoClaim(OPT_INPUT) ||314      (!Args.hasArgNoClaim(OPT_d) && !Args.hasArgNoClaim(OPT_l) &&315       !Args.hasArgNoClaim(OPT_I))) {316    Table.printHelp(outs(), "llvm-dlltool [options] file...", "llvm-dlltool",317                    false);318    llvm::outs()319        << "\nTARGETS: i386, i386:x86-64, arm, arm64, arm64ec, r4000\n";320    return 1;321  }322 323  for (auto *Arg : Args.filtered(OPT_UNKNOWN))324    llvm::errs() << "ignoring unknown argument: " << Arg->getAsString(Args)325                 << "\n";326 327  if (Args.hasArg(OPT_I)) {328    return doIdentify(Args.getLastArg(OPT_I)->getValue(),329                      Args.hasArg(OPT_identify_strict));330  }331 332  if (!Args.hasArg(OPT_d)) {333    llvm::errs() << "no definition file specified\n";334    return 1;335  }336 337  COFF::MachineTypes Machine = getDefaultMachine();338  if (std::optional<std::string> Prefix = getPrefix(ArgsArr[0])) {339    Triple T(*Prefix);340    if (T.getArch() != Triple::UnknownArch)341      Machine = getMachine(T);342  }343  if (auto *Arg = Args.getLastArg(OPT_m))344    Machine = getEmulation(Arg->getValue());345 346  if (Machine == IMAGE_FILE_MACHINE_UNKNOWN) {347    llvm::errs() << "unknown target\n";348    return 1;349  }350 351  bool AddUnderscores = !Args.hasArg(OPT_no_leading_underscore);352 353  std::string OutputFile;354  if (auto *Arg = Args.getLastArg(OPT_D))355    OutputFile = Arg->getValue();356 357  std::vector<COFFShortExport> Exports, NativeExports;358 359  if (Args.hasArg(OPT_N)) {360    if (!isArm64EC(Machine)) {361      llvm::errs() << "native .def file is supported only on arm64ec target\n";362      return 1;363    }364    if (!parseModuleDefinition(Args.getLastArg(OPT_N)->getValue(),365                               IMAGE_FILE_MACHINE_ARM64, AddUnderscores,366                               NativeExports, OutputFile))367      return 1;368  }369 370  if (!parseModuleDefinition(Args.getLastArg(OPT_d)->getValue(), Machine,371                             AddUnderscores, Exports, OutputFile))372    return 1;373 374  if (OutputFile.empty()) {375    llvm::errs() << "no DLL name specified\n";376    return 1;377  }378 379  if (Machine == IMAGE_FILE_MACHINE_I386 && Args.hasArg(OPT_k)) {380    for (COFFShortExport &E : Exports) {381      if (!E.ImportName.empty() || (!E.Name.empty() && E.Name[0] == '?'))382        continue;383      E.SymbolName = E.Name;384      // Trim off the trailing decoration. Symbols will always have a385      // starting prefix here (either _ for cdecl/stdcall, @ for fastcall386      // or ? for C++ functions). Vectorcall functions won't have any387      // fixed prefix, but the function base name will still be at least388      // one char.389      E.Name = E.Name.substr(0, E.Name.find('@', 1));390      // By making sure E.SymbolName != E.Name for decorated symbols,391      // writeImportLibrary writes these symbols with the type392      // IMPORT_NAME_UNDECORATE.393    }394  }395 396  std::string Path = std::string(Args.getLastArgValue(OPT_l));397  if (!Path.empty()) {398    if (Error E = writeImportLibrary(OutputFile, Path, Exports, Machine,399                                     /*MinGW=*/true, NativeExports)) {400      handleAllErrors(std::move(E), [&](const ErrorInfoBase &EI) {401        llvm::errs() << EI.message() << "\n";402      });403      return 1;404    }405  }406  return 0;407}408