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