brintos

brintos / llvm-project-archived public Read only

0
0
Text · 32.1 KiB · 42c7f93 Raw
945 lines · cpp
1//===- DriverUtils.cpp ----------------------------------------------------===//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// This file contains utility functions for the driver. Because there10// are so many small functions, we created this separate file to make11// Driver.cpp less cluttered.12//13//===----------------------------------------------------------------------===//14 15#include "COFFLinkerContext.h"16#include "Driver.h"17#include "Symbols.h"18#include "lld/Common/ErrorHandler.h"19#include "lld/Common/Memory.h"20#include "llvm/ADT/StringExtras.h"21#include "llvm/ADT/StringSwitch.h"22#include "llvm/BinaryFormat/COFF.h"23#include "llvm/IR/Mangler.h"24#include "llvm/Object/COFF.h"25#include "llvm/Object/WindowsResource.h"26#include "llvm/Option/Arg.h"27#include "llvm/Option/ArgList.h"28#include "llvm/Option/Option.h"29#include "llvm/Support/CommandLine.h"30#include "llvm/Support/MathExtras.h"31#include "llvm/Support/Process.h"32#include "llvm/Support/Program.h"33#include "llvm/Support/raw_ostream.h"34#include "llvm/WindowsManifest/WindowsManifestMerger.h"35#include <memory>36#include <optional>37 38using namespace llvm::COFF;39using namespace llvm::object;40using namespace llvm::opt;41using namespace llvm;42using llvm::sys::Process;43 44namespace lld {45namespace coff {46namespace {47 48const uint16_t SUBLANG_ENGLISH_US = 0x0409;49const uint16_t RT_MANIFEST = 24;50 51class Executor {52public:53  explicit Executor(StringRef s) : prog(saver().save(s)) {}54  void add(StringRef s) { args.push_back(saver().save(s)); }55  void add(std::string &s) { args.push_back(saver().save(s)); }56  void add(Twine s) { args.push_back(saver().save(s)); }57  void add(const char *s) { args.push_back(saver().save(s)); }58 59  void run() {60    ErrorOr<std::string> exeOrErr = sys::findProgramByName(prog);61    if (auto ec = exeOrErr.getError())62      fatal("unable to find " + prog + " in PATH: " + ec.message());63    StringRef exe = saver().save(*exeOrErr);64    args.insert(args.begin(), exe);65 66    if (sys::ExecuteAndWait(args[0], args) != 0)67      fatal("ExecuteAndWait failed: " +68            llvm::join(args.begin(), args.end(), " "));69  }70 71private:72  StringRef prog;73  std::vector<StringRef> args;74};75 76} // anonymous namespace77 78// Parses a string in the form of "<integer>[,<integer>]".79void LinkerDriver::parseNumbers(StringRef arg, uint64_t *addr, uint64_t *size) {80  auto [s1, s2] = arg.split(',');81  if (s1.getAsInteger(0, *addr))82    Fatal(ctx) << "invalid number: " << s1;83  if (size && !s2.empty() && s2.getAsInteger(0, *size))84    Fatal(ctx) << "invalid number: " << s2;85}86 87// Parses a string in the form of "<integer>[.<integer>]".88// If second number is not present, Minor is set to 0.89void LinkerDriver::parseVersion(StringRef arg, uint32_t *major,90                                uint32_t *minor) {91  auto [s1, s2] = arg.split('.');92  if (s1.getAsInteger(10, *major))93    Fatal(ctx) << "invalid number: " << s1;94  *minor = 0;95  if (!s2.empty() && s2.getAsInteger(10, *minor))96    Fatal(ctx) << "invalid number: " << s2;97}98 99void LinkerDriver::parseGuard(StringRef fullArg) {100  SmallVector<StringRef, 1> splitArgs;101  fullArg.split(splitArgs, ",");102  for (StringRef arg : splitArgs) {103    if (arg.equals_insensitive("no"))104      ctx.config.guardCF = GuardCFLevel::Off;105    else if (arg.equals_insensitive("nolongjmp"))106      ctx.config.guardCF &= ~GuardCFLevel::LongJmp;107    else if (arg.equals_insensitive("noehcont"))108      ctx.config.guardCF &= ~GuardCFLevel::EHCont;109    else if (arg.equals_insensitive("cf") || arg.equals_insensitive("longjmp"))110      ctx.config.guardCF |= GuardCFLevel::CF | GuardCFLevel::LongJmp;111    else if (arg.equals_insensitive("ehcont"))112      ctx.config.guardCF |= GuardCFLevel::CF | GuardCFLevel::EHCont;113    else114      Fatal(ctx) << "invalid argument to /guard: " << arg;115  }116}117 118// Parses a string in the form of "<subsystem>[,<integer>[.<integer>]]".119void LinkerDriver::parseSubsystem(StringRef arg, WindowsSubsystem *sys,120                                  uint32_t *major, uint32_t *minor,121                                  bool *gotVersion) {122  auto [sysStr, ver] = arg.split(',');123  std::string sysStrLower = sysStr.lower();124  *sys = StringSwitch<WindowsSubsystem>(sysStrLower)125    .Case("boot_application", IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION)126    .Case("console", IMAGE_SUBSYSTEM_WINDOWS_CUI)127    .Case("default", IMAGE_SUBSYSTEM_UNKNOWN)128    .Case("efi_application", IMAGE_SUBSYSTEM_EFI_APPLICATION)129    .Case("efi_boot_service_driver", IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER)130    .Case("efi_rom", IMAGE_SUBSYSTEM_EFI_ROM)131    .Case("efi_runtime_driver", IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER)132    .Case("native", IMAGE_SUBSYSTEM_NATIVE)133    .Case("posix", IMAGE_SUBSYSTEM_POSIX_CUI)134    .Case("windows", IMAGE_SUBSYSTEM_WINDOWS_GUI)135    .Default(IMAGE_SUBSYSTEM_UNKNOWN);136  if (*sys == IMAGE_SUBSYSTEM_UNKNOWN && sysStrLower != "default")137    Fatal(ctx) << "unknown subsystem: " << sysStr;138  if (!ver.empty())139    parseVersion(ver, major, minor);140  if (gotVersion)141    *gotVersion = !ver.empty();142}143 144// Parse a string of the form of "<from>=<to>".145// Results are directly written to Config.146void LinkerDriver::parseMerge(StringRef s) {147  auto [from, to] = s.split('=');148  if (from.empty() || to.empty())149    Fatal(ctx) << "/merge: invalid argument: " << s;150  if (from == ".rsrc" || to == ".rsrc")151    Fatal(ctx) << "/merge: cannot merge '.rsrc' with any section";152  if (from == ".reloc" || to == ".reloc")153    Fatal(ctx) << "/merge: cannot merge '.reloc' with any section";154  auto pair = ctx.config.merge.insert(std::make_pair(from, to));155  bool inserted = pair.second;156  if (!inserted) {157    StringRef existing = pair.first->second;158    if (existing != to)159      Warn(ctx) << s << ": already merged into " << existing;160  }161}162 163void LinkerDriver::parsePDBPageSize(StringRef s) {164  int v;165  if (s.getAsInteger(0, v)) {166    Err(ctx) << "/pdbpagesize: invalid argument: " << s;167    return;168  }169  if (v != 4096 && v != 8192 && v != 16384 && v != 32768) {170    Err(ctx) << "/pdbpagesize: invalid argument: " << s;171    return;172  }173 174  ctx.config.pdbPageSize = v;175}176 177static uint32_t parseSectionAttributes(COFFLinkerContext &ctx, StringRef s) {178  uint32_t ret = 0;179  for (char c : s.lower()) {180    switch (c) {181    case 'd':182      ret |= IMAGE_SCN_MEM_DISCARDABLE;183      break;184    case 'e':185      ret |= IMAGE_SCN_MEM_EXECUTE;186      break;187    case 'k':188      ret |= IMAGE_SCN_MEM_NOT_CACHED;189      break;190    case 'p':191      ret |= IMAGE_SCN_MEM_NOT_PAGED;192      break;193    case 'r':194      ret |= IMAGE_SCN_MEM_READ;195      break;196    case 's':197      ret |= IMAGE_SCN_MEM_SHARED;198      break;199    case 'w':200      ret |= IMAGE_SCN_MEM_WRITE;201      break;202    default:203      Fatal(ctx) << "/section: invalid argument: " << s;204    }205  }206  return ret;207}208 209// Parses /section option argument.210void LinkerDriver::parseSection(StringRef s) {211  auto [name, attrs] = s.split(',');212  if (name.empty() || attrs.empty())213    Fatal(ctx) << "/section: invalid argument: " << s;214  ctx.config.section[name] = parseSectionAttributes(ctx, attrs);215}216 217// Parses /sectionlayout: option argument.218void LinkerDriver::parseSectionLayout(StringRef path) {219  if (path.starts_with("@"))220    path = path.substr(1);221  std::unique_ptr<MemoryBuffer> layoutFile =222      CHECK(MemoryBuffer::getFile(path), "could not open " + path);223  StringRef content = layoutFile->getBuffer();224  int index = 0;225 226  while (!content.empty()) {227    size_t pos = content.find_first_of("\r\n");228    StringRef line;229 230    if (pos == StringRef::npos) {231      line = content;232      content = StringRef();233    } else {234      line = content.substr(0, pos);235      content = content.substr(pos).ltrim("\r\n");236    }237 238    line = line.trim();239    if (line.empty())240      continue;241 242    StringRef sectionName = line.split(' ').first;243 244    if (ctx.config.sectionOrder.count(sectionName.str())) {245      Warn(ctx) << "duplicate section '" << sectionName.str()246                << "' in section layout file, ignoring";247      continue;248    }249 250    ctx.config.sectionOrder[sectionName.str()] = index++;251  }252}253 254void LinkerDriver::parseDosStub(StringRef path) {255  std::unique_ptr<MemoryBuffer> stub =256      CHECK(MemoryBuffer::getFile(path), "could not open " + path);257  size_t bufferSize = stub->getBufferSize();258  const char *bufferStart = stub->getBufferStart();259  // MS link.exe compatibility:260  // 1. stub must be greater than or equal to 64 bytes261  // 2. stub must start with a valid dos signature 'MZ'262  if (bufferSize < 64)263    Err(ctx) << "/stub: stub must be greater than or equal to 64 bytes: "264             << path;265  if (bufferStart[0] != 'M' || bufferStart[1] != 'Z')266    Err(ctx) << "/stub: invalid DOS signature: " << path;267  ctx.config.dosStub = std::move(stub);268}269 270// Parses /functionpadmin option argument.271void LinkerDriver::parseFunctionPadMin(llvm::opt::Arg *a) {272  StringRef arg = a->getNumValues() ? a->getValue() : "";273  if (!arg.empty()) {274    // Optional padding in bytes is given.275    if (arg.getAsInteger(0, ctx.config.functionPadMin))276      Err(ctx) << "/functionpadmin: invalid argument: " << arg;277    return;278  }279  // No optional argument given.280  // Set default padding based on machine, similar to link.exe.281  // There is no default padding for ARM platforms.282  if (ctx.config.machine == I386) {283    ctx.config.functionPadMin = 5;284  } else if (ctx.config.machine == AMD64) {285    ctx.config.functionPadMin = 6;286  } else {287    Err(ctx) << "/functionpadmin: invalid argument for this machine: " << arg;288  }289}290 291// Parses /dependentloadflag option argument.292void LinkerDriver::parseDependentLoadFlags(llvm::opt::Arg *a) {293  StringRef arg = a->getNumValues() ? a->getValue() : "";294  if (!arg.empty()) {295    if (arg.getAsInteger(0, ctx.config.dependentLoadFlags))296      Err(ctx) << "/dependentloadflag: invalid argument: " << arg;297    return;298  }299  // MSVC linker reports error "no argument specified", although MSDN describes300  // argument as optional.301  Err(ctx) << "/dependentloadflag: no argument specified";302}303 304// Parses a string in the form of "EMBED[,=<integer>]|NO".305// Results are directly written to306// Config.307void LinkerDriver::parseManifest(StringRef arg) {308  if (arg.equals_insensitive("no")) {309    ctx.config.manifest = Configuration::No;310    return;311  }312  if (!arg.starts_with_insensitive("embed"))313    Fatal(ctx) << "invalid option " << arg;314  ctx.config.manifest = Configuration::Embed;315  arg = arg.substr(strlen("embed"));316  if (arg.empty())317    return;318  if (!arg.starts_with_insensitive(",id="))319    Fatal(ctx) << "invalid option " << arg;320  arg = arg.substr(strlen(",id="));321  if (arg.getAsInteger(0, ctx.config.manifestID))322    Fatal(ctx) << "invalid option " << arg;323}324 325// Parses a string in the form of "level=<string>|uiAccess=<string>|NO".326// Results are directly written to Config.327void LinkerDriver::parseManifestUAC(StringRef arg) {328  if (arg.equals_insensitive("no")) {329    ctx.config.manifestUAC = false;330    return;331  }332  for (;;) {333    arg = arg.ltrim();334    if (arg.empty())335      return;336    if (arg.consume_front_insensitive("level=")) {337      std::tie(ctx.config.manifestLevel, arg) = arg.split(" ");338      continue;339    }340    if (arg.consume_front_insensitive("uiaccess=")) {341      std::tie(ctx.config.manifestUIAccess, arg) = arg.split(" ");342      continue;343    }344    Fatal(ctx) << "invalid option " << arg;345  }346}347 348// Parses a string in the form of "cd|net[,(cd|net)]*"349// Results are directly written to Config.350void LinkerDriver::parseSwaprun(StringRef arg) {351  do {352    auto [swaprun, newArg] = arg.split(',');353    if (swaprun.equals_insensitive("cd"))354      ctx.config.swaprunCD = true;355    else if (swaprun.equals_insensitive("net"))356      ctx.config.swaprunNet = true;357    else if (swaprun.empty())358      Err(ctx) << "/swaprun: missing argument";359    else360      Err(ctx) << "/swaprun: invalid argument: " << swaprun;361    // To catch trailing commas, e.g. `/spawrun:cd,`362    if (newArg.empty() && arg.ends_with(","))363      Err(ctx) << "/swaprun: missing argument";364    arg = newArg;365  } while (!arg.empty());366}367 368void LinkerDriver::parseSameAddress(StringRef arg) {369  auto mangledName = getArm64ECMangledFunctionName(arg);370  Symbol *sym = ctx.symtab.addUndefined(mangledName ? *mangledName : arg);371 372  // MSVC appears to generate thunks even for non-hybrid ARM64EC images.373  // As a side effect, the native symbol is pulled in. Since this is used374  // in the CRT for thread-local constructors, it results in the image375  // containing unnecessary native code. As these thunks don't appear to376  // be useful, we limit this behavior to actual hybrid targets. This may377  // change if compatibility becomes necessary.378  if (ctx.config.machine != ARM64X)379    return;380  Symbol *nativeSym = ctx.hybridSymtab->addUndefined(arg);381  ctx.config.sameAddresses.emplace_back(sym, nativeSym);382}383 384// An RAII temporary file class that automatically removes a temporary file.385namespace {386class TemporaryFile {387public:388  TemporaryFile(COFFLinkerContext &ctx, StringRef prefix, StringRef extn,389                StringRef contents = "")390      : ctx(ctx) {391    SmallString<128> s;392    if (auto ec = sys::fs::createTemporaryFile("lld-" + prefix, extn, s))393      Fatal(ctx) << "cannot create a temporary file: " << ec.message();394    path = std::string(s);395 396    if (!contents.empty()) {397      std::error_code ec;398      raw_fd_ostream os(path, ec, sys::fs::OF_None);399      if (ec)400        Fatal(ctx) << "failed to open " << path << ": " << ec.message();401      os << contents;402    }403  }404 405  TemporaryFile(TemporaryFile &&obj) noexcept : ctx(obj.ctx) {406    std::swap(path, obj.path);407  }408 409  ~TemporaryFile() {410    if (path.empty())411      return;412    if (sys::fs::remove(path))413      Fatal(ctx) << "failed to remove " << path;414  }415 416  // Returns a memory buffer of this temporary file.417  // Note that this function does not leave the file open,418  // so it is safe to remove the file immediately after this function419  // is called (you cannot remove an opened file on Windows.)420  std::unique_ptr<MemoryBuffer> getMemoryBuffer() {421    // IsVolatile=true forces MemoryBuffer to not use mmap().422    return CHECK(MemoryBuffer::getFile(path, /*IsText=*/false,423                                       /*RequiresNullTerminator=*/false,424                                       /*IsVolatile=*/true),425                 "could not open " + path);426  }427 428  COFFLinkerContext &ctx;429  std::string path;430};431}432 433std::string LinkerDriver::createDefaultXml() {434  std::string ret;435  raw_string_ostream os(ret);436 437  // Emit the XML. Note that we do *not* verify that the XML attributes are438  // syntactically correct. This is intentional for link.exe compatibility.439  os << "<?xml version=\"1.0\" standalone=\"yes\"?>\n"440     << "<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\"\n"441     << "          manifestVersion=\"1.0\">\n";442  if (ctx.config.manifestUAC) {443    os << "  <trustInfo xmlns=\"urn:schemas-microsoft-com:asm.v3\">\n"444       << "    <security>\n"445       << "      <requestedPrivileges>\n"446       << "         <requestedExecutionLevel level=" << ctx.config.manifestLevel447       << " uiAccess=" << ctx.config.manifestUIAccess << "/>\n"448       << "      </requestedPrivileges>\n"449       << "    </security>\n"450       << "  </trustInfo>\n";451  }452  for (auto manifestDependency : ctx.config.manifestDependencies) {453    os << "  <dependency>\n"454       << "    <dependentAssembly>\n"455       << "      <assemblyIdentity " << manifestDependency << " />\n"456       << "    </dependentAssembly>\n"457       << "  </dependency>\n";458  }459  os << "</assembly>\n";460  return ret;461}462 463std::string464LinkerDriver::createManifestXmlWithInternalMt(StringRef defaultXml) {465  std::unique_ptr<MemoryBuffer> defaultXmlCopy =466      MemoryBuffer::getMemBufferCopy(defaultXml);467 468  windows_manifest::WindowsManifestMerger merger;469  if (auto e = merger.merge(*defaultXmlCopy))470    Fatal(ctx) << "internal manifest tool failed on default xml: "471               << toString(std::move(e));472 473  for (StringRef filename : ctx.config.manifestInput) {474    std::unique_ptr<MemoryBuffer> manifest =475        check(MemoryBuffer::getFile(filename));476    // Call takeBuffer to include in /reproduce: output if applicable.477    if (auto e = merger.merge(takeBuffer(std::move(manifest))))478      Fatal(ctx) << "internal manifest tool failed on file " << filename << ": "479                 << toString(std::move(e));480  }481 482  return std::string(merger.getMergedManifest()->getBuffer());483}484 485std::string486LinkerDriver::createManifestXmlWithExternalMt(StringRef defaultXml) {487  // Create the default manifest file as a temporary file.488  TemporaryFile Default(ctx, "defaultxml", "manifest");489  std::error_code ec;490  raw_fd_ostream os(Default.path, ec, sys::fs::OF_TextWithCRLF);491  if (ec)492    Fatal(ctx) << "failed to open " << Default.path << ": " << ec.message();493  os << defaultXml;494  os.close();495 496  // Merge user-supplied manifests if they are given.  Since libxml2 is not497  // enabled, we must shell out to Microsoft's mt.exe tool.498  TemporaryFile user(ctx, "user", "manifest");499 500  Executor e("mt.exe");501  e.add("/manifest");502  e.add(Default.path);503  for (StringRef filename : ctx.config.manifestInput) {504    e.add("/manifest");505    e.add(filename);506 507    // Manually add the file to the /reproduce: tar if needed.508    if (tar)509      if (auto mbOrErr = MemoryBuffer::getFile(filename))510        takeBuffer(std::move(*mbOrErr));511  }512  e.add("/nologo");513  e.add("/out:" + StringRef(user.path));514  e.run();515 516  return std::string(517      CHECK(MemoryBuffer::getFile(user.path), "could not open " + user.path)518          .get()519          ->getBuffer());520}521 522std::string LinkerDriver::createManifestXml() {523  std::string defaultXml = createDefaultXml();524  if (ctx.config.manifestInput.empty())525    return defaultXml;526 527  if (windows_manifest::isAvailable())528    return createManifestXmlWithInternalMt(defaultXml);529 530  return createManifestXmlWithExternalMt(defaultXml);531}532 533std::unique_ptr<WritableMemoryBuffer>534LinkerDriver::createMemoryBufferForManifestRes(size_t manifestSize) {535  size_t resSize = alignTo(536      object::WIN_RES_MAGIC_SIZE + object::WIN_RES_NULL_ENTRY_SIZE +537          sizeof(object::WinResHeaderPrefix) + sizeof(object::WinResIDs) +538          sizeof(object::WinResHeaderSuffix) + manifestSize,539      object::WIN_RES_DATA_ALIGNMENT);540  return WritableMemoryBuffer::getNewMemBuffer(resSize, ctx.config.outputFile +541                                                            ".manifest.res");542}543 544static void writeResFileHeader(char *&buf) {545  memcpy(buf, COFF::WinResMagic, sizeof(COFF::WinResMagic));546  buf += sizeof(COFF::WinResMagic);547  memset(buf, 0, object::WIN_RES_NULL_ENTRY_SIZE);548  buf += object::WIN_RES_NULL_ENTRY_SIZE;549}550 551static void writeResEntryHeader(char *&buf, size_t manifestSize,552                                int manifestID) {553  // Write the prefix.554  auto *prefix = reinterpret_cast<object::WinResHeaderPrefix *>(buf);555  prefix->DataSize = manifestSize;556  prefix->HeaderSize = sizeof(object::WinResHeaderPrefix) +557                       sizeof(object::WinResIDs) +558                       sizeof(object::WinResHeaderSuffix);559  buf += sizeof(object::WinResHeaderPrefix);560 561  // Write the Type/Name IDs.562  auto *iDs = reinterpret_cast<object::WinResIDs *>(buf);563  iDs->setType(RT_MANIFEST);564  iDs->setName(manifestID);565  buf += sizeof(object::WinResIDs);566 567  // Write the suffix.568  auto *suffix = reinterpret_cast<object::WinResHeaderSuffix *>(buf);569  suffix->DataVersion = 0;570  suffix->MemoryFlags = object::WIN_RES_PURE_MOVEABLE;571  suffix->Language = SUBLANG_ENGLISH_US;572  suffix->Version = 0;573  suffix->Characteristics = 0;574  buf += sizeof(object::WinResHeaderSuffix);575}576 577// Create a resource file containing a manifest XML.578std::unique_ptr<MemoryBuffer> LinkerDriver::createManifestRes() {579  std::string manifest = createManifestXml();580 581  std::unique_ptr<WritableMemoryBuffer> res =582      createMemoryBufferForManifestRes(manifest.size());583 584  char *buf = res->getBufferStart();585  writeResFileHeader(buf);586  writeResEntryHeader(buf, manifest.size(), ctx.config.manifestID);587 588  // Copy the manifest data into the .res file.589  std::copy(manifest.begin(), manifest.end(), buf);590  return std::move(res);591}592 593void LinkerDriver::createSideBySideManifest() {594  std::string path = std::string(ctx.config.manifestFile);595  if (path == "")596    path = ctx.config.outputFile + ".manifest";597  std::error_code ec;598  raw_fd_ostream out(path, ec, sys::fs::OF_TextWithCRLF);599  if (ec)600    Fatal(ctx) << "failed to create manifest: " << ec.message();601  out << createManifestXml();602}603 604// Parse a string in the form of605// "<name>[=<internalname>][,@ordinal[,NONAME]][,DATA][,PRIVATE]"606// or "<name>=<dllname>.<name>".607// Used for parsing /export arguments.608Export LinkerDriver::parseExport(StringRef arg) {609  Export e;610  e.source = ExportSource::Export;611 612  StringRef rest;613  std::tie(e.name, rest) = arg.split(",");614  if (e.name.empty())615    goto err;616 617  if (e.name.contains('=')) {618    auto [x, y] = e.name.split("=");619 620    // If "<name>=<dllname>.<name>".621    if (y.contains(".")) {622      e.name = x;623      e.forwardTo = y;624    } else {625      e.extName = x;626      e.name = y;627      if (e.name.empty())628        goto err;629    }630  }631 632  // Optional parameters633  // "[,@ordinal[,NONAME]][,DATA][,PRIVATE][,EXPORTAS,exportname]"634  while (!rest.empty()) {635    StringRef tok;636    std::tie(tok, rest) = rest.split(",");637    if (tok.equals_insensitive("noname")) {638      if (e.ordinal == 0)639        goto err;640      e.noname = true;641      continue;642    }643    if (tok.equals_insensitive("data")) {644      e.data = true;645      continue;646    }647    if (tok.equals_insensitive("constant")) {648      e.constant = true;649      continue;650    }651    if (tok.equals_insensitive("private")) {652      e.isPrivate = true;653      continue;654    }655    if (tok.equals_insensitive("exportas")) {656      if (!rest.empty() && !rest.contains(','))657        e.exportAs = rest;658      else659        Err(ctx) << "invalid EXPORTAS value: " << rest;660      break;661    }662    if (tok.starts_with("@")) {663      int32_t ord;664      if (tok.substr(1).getAsInteger(0, ord))665        goto err;666      if (ord <= 0 || 65535 < ord)667        goto err;668      e.ordinal = ord;669      continue;670    }671    goto err;672  }673  return e;674 675err:676  Fatal(ctx) << "invalid /export: " << arg;677  llvm_unreachable("");678}679 680// Parses a string in the form of "key=value" and check681// if value matches previous values for the same key.682void LinkerDriver::checkFailIfMismatch(StringRef arg, InputFile *source) {683  auto [k, v] = arg.split('=');684  if (k.empty() || v.empty())685    Fatal(ctx) << "/failifmismatch: invalid argument: " << arg;686  std::pair<StringRef, InputFile *> existing = ctx.config.mustMatch[k];687  if (!existing.first.empty() && v != existing.first) {688    std::string sourceStr = source ? toString(source) : "cmd-line";689    std::string existingStr =690        existing.second ? toString(existing.second) : "cmd-line";691    Fatal(ctx) << "/failifmismatch: mismatch detected for '" << k << "':\n>>> "692               << existingStr << " has value " << existing.first << "\n>>> "693               << sourceStr << " has value " << v;694  }695  ctx.config.mustMatch[k] = {v, source};696}697 698// Convert Windows resource files (.res files) to a .obj file.699// Does what cvtres.exe does, but in-process and cross-platform.700MemoryBufferRef LinkerDriver::convertResToCOFF(ArrayRef<MemoryBufferRef> mbs,701                                               ArrayRef<ObjFile *> objs) {702  object::WindowsResourceParser parser(/* MinGW */ ctx.config.mingw);703 704  std::vector<std::string> duplicates;705  for (MemoryBufferRef mb : mbs) {706    std::unique_ptr<object::Binary> bin = check(object::createBinary(mb));707    object::WindowsResource *rf = dyn_cast<object::WindowsResource>(bin.get());708    if (!rf)709      Fatal(ctx) << "cannot compile non-resource file as resource";710 711    if (auto ec = parser.parse(rf, duplicates))712      Fatal(ctx) << toString(std::move(ec));713  }714 715  // Note: This processes all .res files before all objs. Ideally they'd be716  // handled in the same order they were linked (to keep the right one, if717  // there are duplicates that are tolerated due to forceMultipleRes).718  for (ObjFile *f : objs) {719    object::ResourceSectionRef rsf;720    if (auto ec = rsf.load(f->getCOFFObj()))721      Fatal(ctx) << toString(f) << ": " << toString(std::move(ec));722 723    if (auto ec = parser.parse(rsf, f->getName(), duplicates))724      Fatal(ctx) << toString(std::move(ec));725  }726 727  if (ctx.config.mingw)728    parser.cleanUpManifests(duplicates);729 730  for (const auto &dupeDiag : duplicates)731    if (ctx.config.forceMultipleRes)732      Warn(ctx) << dupeDiag;733    else734      Err(ctx) << dupeDiag;735 736  Expected<std::unique_ptr<MemoryBuffer>> e =737      llvm::object::writeWindowsResourceCOFF(ctx.config.machine, parser,738                                             ctx.config.timestamp);739  if (!e)740    Fatal(ctx) << "failed to write .res to COFF: " << toString(e.takeError());741 742  MemoryBufferRef mbref = **e;743  make<std::unique_ptr<MemoryBuffer>>(std::move(*e)); // take ownership744  return mbref;745}746 747// Create OptTable748 749#define OPTTABLE_STR_TABLE_CODE750#include "Options.inc"751#undef OPTTABLE_STR_TABLE_CODE752 753// Create prefix string literals used in Options.td754#define OPTTABLE_PREFIXES_TABLE_CODE755#include "Options.inc"756#undef OPTTABLE_PREFIXES_TABLE_CODE757 758// Create table mapping all options defined in Options.td759static constexpr llvm::opt::OptTable::Info infoTable[] = {760#define OPTION(...) LLVM_CONSTRUCT_OPT_INFO(__VA_ARGS__),761#include "Options.inc"762#undef OPTION763};764 765COFFOptTable::COFFOptTable()766    : GenericOptTable(OptionStrTable, OptionPrefixesTable, infoTable, true) {}767 768// Set color diagnostics according to --color-diagnostics={auto,always,never}769// or --no-color-diagnostics flags.770static void handleColorDiagnostics(COFFLinkerContext &ctx,771                                   opt::InputArgList &args) {772  auto *arg = args.getLastArg(OPT_color_diagnostics, OPT_color_diagnostics_eq,773                              OPT_no_color_diagnostics);774  if (!arg)775    return;776  if (arg->getOption().getID() == OPT_color_diagnostics) {777    ctx.e.errs().enable_colors(true);778  } else if (arg->getOption().getID() == OPT_no_color_diagnostics) {779    ctx.e.errs().enable_colors(false);780  } else {781    StringRef s = arg->getValue();782    if (s == "always")783      ctx.e.errs().enable_colors(true);784    else if (s == "never")785      ctx.e.errs().enable_colors(false);786    else if (s != "auto")787      Err(ctx) << "unknown option: --color-diagnostics=" << s;788  }789}790 791static cl::TokenizerCallback getQuotingStyle(COFFLinkerContext &ctx,792                                             opt::InputArgList &args) {793  if (auto *arg = args.getLastArg(OPT_rsp_quoting)) {794    StringRef s = arg->getValue();795    if (s != "windows" && s != "posix")796      Err(ctx) << "invalid response file quoting: " << s;797    if (s == "windows")798      return cl::TokenizeWindowsCommandLine;799    return cl::TokenizeGNUCommandLine;800  }801  // The COFF linker always defaults to Windows quoting.802  return cl::TokenizeWindowsCommandLine;803}804 805ArgParser::ArgParser(COFFLinkerContext &c) : ctx(c) {}806 807// Parses a given list of options.808opt::InputArgList ArgParser::parse(ArrayRef<const char *> argv) {809  // Make InputArgList from string vectors.810  unsigned missingIndex;811  unsigned missingCount;812 813  // We need to get the quoting style for response files before parsing all814  // options so we parse here before and ignore all the options but815  // --rsp-quoting and /lldignoreenv.816  // (This means --rsp-quoting can't be added through %LINK%.)817  opt::InputArgList args =818      ctx.optTable.ParseArgs(argv, missingIndex, missingCount);819 820  // Expand response files (arguments in the form of @<filename>) and insert821  // flags from %LINK% and %_LINK_%, and then parse the argument again.822  SmallVector<const char *, 256> expandedArgv(argv.data(),823                                              argv.data() + argv.size());824  if (!args.hasArg(OPT_lldignoreenv))825    addLINK(expandedArgv);826  cl::ExpandResponseFiles(saver(), getQuotingStyle(ctx, args), expandedArgv);827  args = ctx.optTable.ParseArgs(ArrayRef(expandedArgv).drop_front(),828                                missingIndex, missingCount);829 830  // Print the real command line if response files are expanded.831  if (args.hasArg(OPT_verbose) && argv.size() != expandedArgv.size()) {832    std::string msg = "Command line:";833    for (const char *s : expandedArgv)834      msg += " " + std::string(s);835    Msg(ctx) << msg;836  }837 838  // Save the command line after response file expansion so we can write it to839  // the PDB if necessary. Mimic MSVC, which skips input files.840  ctx.config.argv = {argv[0]};841  for (opt::Arg *arg : args) {842    if (arg->getOption().getKind() != opt::Option::InputClass) {843      ctx.config.argv.emplace_back(args.getArgString(arg->getIndex()));844    }845  }846 847  // Handle /WX early since it converts missing argument warnings to errors.848  ctx.e.fatalWarnings = args.hasFlag(OPT_WX, OPT_WX_no, false);849 850  if (missingCount)851    Fatal(ctx) << args.getArgString(missingIndex) << ": missing argument";852 853  handleColorDiagnostics(ctx, args);854 855  for (opt::Arg *arg : args.filtered(OPT_UNKNOWN)) {856    std::string nearest;857    if (ctx.optTable.findNearest(arg->getAsString(args), nearest) > 1)858      Warn(ctx) << "ignoring unknown argument '" << arg->getAsString(args)859                << "'";860    else861      Warn(ctx) << "ignoring unknown argument '" << arg->getAsString(args)862                << "', did you mean '" << nearest << "'";863  }864 865  if (args.hasArg(OPT_link))866    Warn(ctx) << "ignoring /link, did you pass it multiple times?";867 868  if (args.hasArg(OPT_lib))869    Warn(ctx) << "ignoring /lib since it's not the first argument";870 871  return args;872}873 874// Tokenizes and parses a given string as command line in .drective section.875ParsedDirectives ArgParser::parseDirectives(StringRef s) {876  ParsedDirectives result;877  SmallVector<const char *, 16> rest;878 879  // Handle /EXPORT and /INCLUDE in a fast path. These directives can appear for880  // potentially every symbol in the object, so they must be handled quickly.881  SmallVector<StringRef, 16> tokens;882  cl::TokenizeWindowsCommandLineNoCopy(s, saver(), tokens);883  for (StringRef tok : tokens) {884    if (tok.starts_with_insensitive("/export:") ||885        tok.starts_with_insensitive("-export:"))886      result.exports.push_back(tok.substr(strlen("/export:")));887    else if (tok.starts_with_insensitive("/include:") ||888             tok.starts_with_insensitive("-include:"))889      result.includes.push_back(tok.substr(strlen("/include:")));890    else if (tok.starts_with_insensitive("/exclude-symbols:") ||891             tok.starts_with_insensitive("-exclude-symbols:"))892      result.excludes.push_back(tok.substr(strlen("/exclude-symbols:")));893    else {894      // Copy substrings that are not valid C strings. The tokenizer may have895      // already copied quoted arguments for us, so those do not need to be896      // copied again.897      bool HasNul = tok.end() != s.end() && tok.data()[tok.size()] == '\0';898      rest.push_back(HasNul ? tok.data() : saver().save(tok).data());899    }900  }901 902  // Make InputArgList from unparsed string vectors.903  unsigned missingIndex;904  unsigned missingCount;905 906  result.args = ctx.optTable.ParseArgs(rest, missingIndex, missingCount);907 908  if (missingCount)909    Fatal(ctx) << result.args.getArgString(missingIndex)910               << ": missing argument";911  for (auto *arg : result.args.filtered(OPT_UNKNOWN))912    Warn(ctx) << "ignoring unknown argument: " << arg->getAsString(result.args);913  return result;914}915 916// link.exe has an interesting feature. If LINK or _LINK_ environment917// variables exist, their contents are handled as command line strings.918// So you can pass extra arguments using them.919void ArgParser::addLINK(SmallVector<const char *, 256> &argv) {920  // Concatenate LINK env and command line arguments, and then parse them.921  if (std::optional<std::string> s = Process::GetEnv("LINK")) {922    std::vector<const char *> v = tokenize(*s);923    argv.insert(std::next(argv.begin()), v.begin(), v.end());924  }925  if (std::optional<std::string> s = Process::GetEnv("_LINK_")) {926    std::vector<const char *> v = tokenize(*s);927    argv.insert(std::next(argv.begin()), v.begin(), v.end());928  }929}930 931std::vector<const char *> ArgParser::tokenize(StringRef s) {932  SmallVector<const char *, 16> tokens;933  cl::TokenizeWindowsCommandLine(s, saver(), tokens);934  return std::vector<const char *>(tokens.begin(), tokens.end());935}936 937void LinkerDriver::printHelp(const char *argv0) {938  ctx.optTable.printHelp(ctx.e.outs(),939                         (std::string(argv0) + " [options] file...").c_str(),940                         "LLVM Linker", false);941}942 943} // namespace coff944} // namespace lld945