|  | //===- DriverUtils.cpp ----------------------------------------------------===// | 
|  | // | 
|  | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | 
|  | // See https://llvm.org/LICENSE.txt for license information. | 
|  | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  | // | 
|  | // This file contains utility functions for the driver. Because there | 
|  | // are so many small functions, we created this separate file to make | 
|  | // Driver.cpp less cluttered. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "Config.h" | 
|  | #include "Driver.h" | 
|  | #include "Symbols.h" | 
|  | #include "lld/Common/ErrorHandler.h" | 
|  | #include "lld/Common/Memory.h" | 
|  | #include "llvm/ADT/Optional.h" | 
|  | #include "llvm/ADT/StringSwitch.h" | 
|  | #include "llvm/BinaryFormat/COFF.h" | 
|  | #include "llvm/Object/COFF.h" | 
|  | #include "llvm/Object/WindowsResource.h" | 
|  | #include "llvm/Option/Arg.h" | 
|  | #include "llvm/Option/ArgList.h" | 
|  | #include "llvm/Option/Option.h" | 
|  | #include "llvm/Support/CommandLine.h" | 
|  | #include "llvm/Support/FileUtilities.h" | 
|  | #include "llvm/Support/MathExtras.h" | 
|  | #include "llvm/Support/Process.h" | 
|  | #include "llvm/Support/Program.h" | 
|  | #include "llvm/Support/raw_ostream.h" | 
|  | #include "llvm/WindowsManifest/WindowsManifestMerger.h" | 
|  | #include <limits> | 
|  | #include <memory> | 
|  |  | 
|  | using namespace llvm::COFF; | 
|  | using namespace llvm; | 
|  | using llvm::sys::Process; | 
|  |  | 
|  | namespace lld { | 
|  | namespace coff { | 
|  | namespace { | 
|  |  | 
|  | const uint16_t SUBLANG_ENGLISH_US = 0x0409; | 
|  | const uint16_t RT_MANIFEST = 24; | 
|  |  | 
|  | class Executor { | 
|  | public: | 
|  | explicit Executor(StringRef s) : prog(saver().save(s)) {} | 
|  | void add(StringRef s) { args.push_back(saver().save(s)); } | 
|  | void add(std::string &s) { args.push_back(saver().save(s)); } | 
|  | void add(Twine s) { args.push_back(saver().save(s)); } | 
|  | void add(const char *s) { args.push_back(saver().save(s)); } | 
|  |  | 
|  | void run() { | 
|  | ErrorOr<std::string> exeOrErr = sys::findProgramByName(prog); | 
|  | if (auto ec = exeOrErr.getError()) | 
|  | fatal("unable to find " + prog + " in PATH: " + ec.message()); | 
|  | StringRef exe = saver().save(*exeOrErr); | 
|  | args.insert(args.begin(), exe); | 
|  |  | 
|  | if (sys::ExecuteAndWait(args[0], args) != 0) | 
|  | fatal("ExecuteAndWait failed: " + | 
|  | llvm::join(args.begin(), args.end(), " ")); | 
|  | } | 
|  |  | 
|  | private: | 
|  | StringRef prog; | 
|  | std::vector<StringRef> args; | 
|  | }; | 
|  |  | 
|  | } // anonymous namespace | 
|  |  | 
|  | // Parses a string in the form of "<integer>[,<integer>]". | 
|  | void parseNumbers(StringRef arg, uint64_t *addr, uint64_t *size) { | 
|  | StringRef s1, s2; | 
|  | std::tie(s1, s2) = arg.split(','); | 
|  | if (s1.getAsInteger(0, *addr)) | 
|  | fatal("invalid number: " + s1); | 
|  | if (size && !s2.empty() && s2.getAsInteger(0, *size)) | 
|  | fatal("invalid number: " + s2); | 
|  | } | 
|  |  | 
|  | // Parses a string in the form of "<integer>[.<integer>]". | 
|  | // If second number is not present, Minor is set to 0. | 
|  | void parseVersion(StringRef arg, uint32_t *major, uint32_t *minor) { | 
|  | StringRef s1, s2; | 
|  | std::tie(s1, s2) = arg.split('.'); | 
|  | if (s1.getAsInteger(10, *major)) | 
|  | fatal("invalid number: " + s1); | 
|  | *minor = 0; | 
|  | if (!s2.empty() && s2.getAsInteger(10, *minor)) | 
|  | fatal("invalid number: " + s2); | 
|  | } | 
|  |  | 
|  | void parseGuard(StringRef fullArg) { | 
|  | SmallVector<StringRef, 1> splitArgs; | 
|  | fullArg.split(splitArgs, ","); | 
|  | for (StringRef arg : splitArgs) { | 
|  | if (arg.equals_insensitive("no")) | 
|  | config->guardCF = GuardCFLevel::Off; | 
|  | else if (arg.equals_insensitive("nolongjmp")) | 
|  | config->guardCF &= ~GuardCFLevel::LongJmp; | 
|  | else if (arg.equals_insensitive("noehcont")) | 
|  | config->guardCF &= ~GuardCFLevel::EHCont; | 
|  | else if (arg.equals_insensitive("cf")) | 
|  | config->guardCF = GuardCFLevel::CF; | 
|  | else if (arg.equals_insensitive("longjmp")) | 
|  | config->guardCF |= GuardCFLevel::CF | GuardCFLevel::LongJmp; | 
|  | else if (arg.equals_insensitive("ehcont")) | 
|  | config->guardCF |= GuardCFLevel::CF | GuardCFLevel::EHCont; | 
|  | else | 
|  | fatal("invalid argument to /guard: " + arg); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Parses a string in the form of "<subsystem>[,<integer>[.<integer>]]". | 
|  | void parseSubsystem(StringRef arg, WindowsSubsystem *sys, uint32_t *major, | 
|  | uint32_t *minor, bool *gotVersion) { | 
|  | StringRef sysStr, ver; | 
|  | std::tie(sysStr, ver) = arg.split(','); | 
|  | std::string sysStrLower = sysStr.lower(); | 
|  | *sys = StringSwitch<WindowsSubsystem>(sysStrLower) | 
|  | .Case("boot_application", IMAGE_SUBSYSTEM_WINDOWS_BOOT_APPLICATION) | 
|  | .Case("console", IMAGE_SUBSYSTEM_WINDOWS_CUI) | 
|  | .Case("default", IMAGE_SUBSYSTEM_UNKNOWN) | 
|  | .Case("efi_application", IMAGE_SUBSYSTEM_EFI_APPLICATION) | 
|  | .Case("efi_boot_service_driver", IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER) | 
|  | .Case("efi_rom", IMAGE_SUBSYSTEM_EFI_ROM) | 
|  | .Case("efi_runtime_driver", IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER) | 
|  | .Case("native", IMAGE_SUBSYSTEM_NATIVE) | 
|  | .Case("posix", IMAGE_SUBSYSTEM_POSIX_CUI) | 
|  | .Case("windows", IMAGE_SUBSYSTEM_WINDOWS_GUI) | 
|  | .Default(IMAGE_SUBSYSTEM_UNKNOWN); | 
|  | if (*sys == IMAGE_SUBSYSTEM_UNKNOWN && sysStrLower != "default") | 
|  | fatal("unknown subsystem: " + sysStr); | 
|  | if (!ver.empty()) | 
|  | parseVersion(ver, major, minor); | 
|  | if (gotVersion) | 
|  | *gotVersion = !ver.empty(); | 
|  | } | 
|  |  | 
|  | // Parse a string of the form of "<from>=<to>". | 
|  | // Results are directly written to Config. | 
|  | void parseAlternateName(StringRef s) { | 
|  | StringRef from, to; | 
|  | std::tie(from, to) = s.split('='); | 
|  | if (from.empty() || to.empty()) | 
|  | fatal("/alternatename: invalid argument: " + s); | 
|  | auto it = config->alternateNames.find(from); | 
|  | if (it != config->alternateNames.end() && it->second != to) | 
|  | fatal("/alternatename: conflicts: " + s); | 
|  | config->alternateNames.insert(it, std::make_pair(from, to)); | 
|  | } | 
|  |  | 
|  | // Parse a string of the form of "<from>=<to>". | 
|  | // Results are directly written to Config. | 
|  | void parseMerge(StringRef s) { | 
|  | StringRef from, to; | 
|  | std::tie(from, to) = s.split('='); | 
|  | if (from.empty() || to.empty()) | 
|  | fatal("/merge: invalid argument: " + s); | 
|  | if (from == ".rsrc" || to == ".rsrc") | 
|  | fatal("/merge: cannot merge '.rsrc' with any section"); | 
|  | if (from == ".reloc" || to == ".reloc") | 
|  | fatal("/merge: cannot merge '.reloc' with any section"); | 
|  | auto pair = config->merge.insert(std::make_pair(from, to)); | 
|  | bool inserted = pair.second; | 
|  | if (!inserted) { | 
|  | StringRef existing = pair.first->second; | 
|  | if (existing != to) | 
|  | warn(s + ": already merged into " + existing); | 
|  | } | 
|  | } | 
|  |  | 
|  | void parsePDBPageSize(StringRef s) { | 
|  | int v; | 
|  | if (s.getAsInteger(0, v)) { | 
|  | error("/pdbpagesize: invalid argument: " + s); | 
|  | return; | 
|  | } | 
|  | if (v != 4096 && v != 8192 && v != 16384 && v != 32768) { | 
|  | error("/pdbpagesize: invalid argument: " + s); | 
|  | return; | 
|  | } | 
|  |  | 
|  | config->pdbPageSize = v; | 
|  | } | 
|  |  | 
|  | static uint32_t parseSectionAttributes(StringRef s) { | 
|  | uint32_t ret = 0; | 
|  | for (char c : s.lower()) { | 
|  | switch (c) { | 
|  | case 'd': | 
|  | ret |= IMAGE_SCN_MEM_DISCARDABLE; | 
|  | break; | 
|  | case 'e': | 
|  | ret |= IMAGE_SCN_MEM_EXECUTE; | 
|  | break; | 
|  | case 'k': | 
|  | ret |= IMAGE_SCN_MEM_NOT_CACHED; | 
|  | break; | 
|  | case 'p': | 
|  | ret |= IMAGE_SCN_MEM_NOT_PAGED; | 
|  | break; | 
|  | case 'r': | 
|  | ret |= IMAGE_SCN_MEM_READ; | 
|  | break; | 
|  | case 's': | 
|  | ret |= IMAGE_SCN_MEM_SHARED; | 
|  | break; | 
|  | case 'w': | 
|  | ret |= IMAGE_SCN_MEM_WRITE; | 
|  | break; | 
|  | default: | 
|  | fatal("/section: invalid argument: " + s); | 
|  | } | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | // Parses /section option argument. | 
|  | void parseSection(StringRef s) { | 
|  | StringRef name, attrs; | 
|  | std::tie(name, attrs) = s.split(','); | 
|  | if (name.empty() || attrs.empty()) | 
|  | fatal("/section: invalid argument: " + s); | 
|  | config->section[name] = parseSectionAttributes(attrs); | 
|  | } | 
|  |  | 
|  | // Parses /aligncomm option argument. | 
|  | void parseAligncomm(StringRef s) { | 
|  | StringRef name, align; | 
|  | std::tie(name, align) = s.split(','); | 
|  | if (name.empty() || align.empty()) { | 
|  | error("/aligncomm: invalid argument: " + s); | 
|  | return; | 
|  | } | 
|  | int v; | 
|  | if (align.getAsInteger(0, v)) { | 
|  | error("/aligncomm: invalid argument: " + s); | 
|  | return; | 
|  | } | 
|  | config->alignComm[std::string(name)] = | 
|  | std::max(config->alignComm[std::string(name)], 1 << v); | 
|  | } | 
|  |  | 
|  | // Parses /functionpadmin option argument. | 
|  | void parseFunctionPadMin(llvm::opt::Arg *a, llvm::COFF::MachineTypes machine) { | 
|  | StringRef arg = a->getNumValues() ? a->getValue() : ""; | 
|  | if (!arg.empty()) { | 
|  | // Optional padding in bytes is given. | 
|  | if (arg.getAsInteger(0, config->functionPadMin)) | 
|  | error("/functionpadmin: invalid argument: " + arg); | 
|  | return; | 
|  | } | 
|  | // No optional argument given. | 
|  | // Set default padding based on machine, similar to link.exe. | 
|  | // There is no default padding for ARM platforms. | 
|  | if (machine == I386) { | 
|  | config->functionPadMin = 5; | 
|  | } else if (machine == AMD64) { | 
|  | config->functionPadMin = 6; | 
|  | } else { | 
|  | error("/functionpadmin: invalid argument for this machine: " + arg); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Parses a string in the form of "EMBED[,=<integer>]|NO". | 
|  | // Results are directly written to Config. | 
|  | void parseManifest(StringRef arg) { | 
|  | if (arg.equals_insensitive("no")) { | 
|  | config->manifest = Configuration::No; | 
|  | return; | 
|  | } | 
|  | if (!arg.startswith_insensitive("embed")) | 
|  | fatal("invalid option " + arg); | 
|  | config->manifest = Configuration::Embed; | 
|  | arg = arg.substr(strlen("embed")); | 
|  | if (arg.empty()) | 
|  | return; | 
|  | if (!arg.startswith_insensitive(",id=")) | 
|  | fatal("invalid option " + arg); | 
|  | arg = arg.substr(strlen(",id=")); | 
|  | if (arg.getAsInteger(0, config->manifestID)) | 
|  | fatal("invalid option " + arg); | 
|  | } | 
|  |  | 
|  | // Parses a string in the form of "level=<string>|uiAccess=<string>|NO". | 
|  | // Results are directly written to Config. | 
|  | void parseManifestUAC(StringRef arg) { | 
|  | if (arg.equals_insensitive("no")) { | 
|  | config->manifestUAC = false; | 
|  | return; | 
|  | } | 
|  | for (;;) { | 
|  | arg = arg.ltrim(); | 
|  | if (arg.empty()) | 
|  | return; | 
|  | if (arg.startswith_insensitive("level=")) { | 
|  | arg = arg.substr(strlen("level=")); | 
|  | std::tie(config->manifestLevel, arg) = arg.split(" "); | 
|  | continue; | 
|  | } | 
|  | if (arg.startswith_insensitive("uiaccess=")) { | 
|  | arg = arg.substr(strlen("uiaccess=")); | 
|  | std::tie(config->manifestUIAccess, arg) = arg.split(" "); | 
|  | continue; | 
|  | } | 
|  | fatal("invalid option " + arg); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Parses a string in the form of "cd|net[,(cd|net)]*" | 
|  | // Results are directly written to Config. | 
|  | void parseSwaprun(StringRef arg) { | 
|  | do { | 
|  | StringRef swaprun, newArg; | 
|  | std::tie(swaprun, newArg) = arg.split(','); | 
|  | if (swaprun.equals_insensitive("cd")) | 
|  | config->swaprunCD = true; | 
|  | else if (swaprun.equals_insensitive("net")) | 
|  | config->swaprunNet = true; | 
|  | else if (swaprun.empty()) | 
|  | error("/swaprun: missing argument"); | 
|  | else | 
|  | error("/swaprun: invalid argument: " + swaprun); | 
|  | // To catch trailing commas, e.g. `/spawrun:cd,` | 
|  | if (newArg.empty() && arg.endswith(",")) | 
|  | error("/swaprun: missing argument"); | 
|  | arg = newArg; | 
|  | } while (!arg.empty()); | 
|  | } | 
|  |  | 
|  | // An RAII temporary file class that automatically removes a temporary file. | 
|  | namespace { | 
|  | class TemporaryFile { | 
|  | public: | 
|  | TemporaryFile(StringRef prefix, StringRef extn, StringRef contents = "") { | 
|  | SmallString<128> s; | 
|  | if (auto ec = sys::fs::createTemporaryFile("lld-" + prefix, extn, s)) | 
|  | fatal("cannot create a temporary file: " + ec.message()); | 
|  | path = std::string(s.str()); | 
|  |  | 
|  | if (!contents.empty()) { | 
|  | std::error_code ec; | 
|  | raw_fd_ostream os(path, ec, sys::fs::OF_None); | 
|  | if (ec) | 
|  | fatal("failed to open " + path + ": " + ec.message()); | 
|  | os << contents; | 
|  | } | 
|  | } | 
|  |  | 
|  | TemporaryFile(TemporaryFile &&obj) { | 
|  | std::swap(path, obj.path); | 
|  | } | 
|  |  | 
|  | ~TemporaryFile() { | 
|  | if (path.empty()) | 
|  | return; | 
|  | if (sys::fs::remove(path)) | 
|  | fatal("failed to remove " + path); | 
|  | } | 
|  |  | 
|  | // Returns a memory buffer of this temporary file. | 
|  | // Note that this function does not leave the file open, | 
|  | // so it is safe to remove the file immediately after this function | 
|  | // is called (you cannot remove an opened file on Windows.) | 
|  | std::unique_ptr<MemoryBuffer> getMemoryBuffer() { | 
|  | // IsVolatile=true forces MemoryBuffer to not use mmap(). | 
|  | return CHECK(MemoryBuffer::getFile(path, /*IsText=*/false, | 
|  | /*RequiresNullTerminator=*/false, | 
|  | /*IsVolatile=*/true), | 
|  | "could not open " + path); | 
|  | } | 
|  |  | 
|  | std::string path; | 
|  | }; | 
|  | } | 
|  |  | 
|  | static std::string createDefaultXml() { | 
|  | std::string ret; | 
|  | raw_string_ostream os(ret); | 
|  |  | 
|  | // Emit the XML. Note that we do *not* verify that the XML attributes are | 
|  | // syntactically correct. This is intentional for link.exe compatibility. | 
|  | os << "<?xml version=\"1.0\" standalone=\"yes\"?>\n" | 
|  | << "<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\"\n" | 
|  | << "          manifestVersion=\"1.0\">\n"; | 
|  | if (config->manifestUAC) { | 
|  | os << "  <trustInfo>\n" | 
|  | << "    <security>\n" | 
|  | << "      <requestedPrivileges>\n" | 
|  | << "         <requestedExecutionLevel level=" << config->manifestLevel | 
|  | << " uiAccess=" << config->manifestUIAccess << "/>\n" | 
|  | << "      </requestedPrivileges>\n" | 
|  | << "    </security>\n" | 
|  | << "  </trustInfo>\n"; | 
|  | } | 
|  | for (auto manifestDependency : config->manifestDependencies) { | 
|  | os << "  <dependency>\n" | 
|  | << "    <dependentAssembly>\n" | 
|  | << "      <assemblyIdentity " << manifestDependency << " />\n" | 
|  | << "    </dependentAssembly>\n" | 
|  | << "  </dependency>\n"; | 
|  | } | 
|  | os << "</assembly>\n"; | 
|  | return os.str(); | 
|  | } | 
|  |  | 
|  | static std::string createManifestXmlWithInternalMt(StringRef defaultXml) { | 
|  | std::unique_ptr<MemoryBuffer> defaultXmlCopy = | 
|  | MemoryBuffer::getMemBufferCopy(defaultXml); | 
|  |  | 
|  | windows_manifest::WindowsManifestMerger merger; | 
|  | if (auto e = merger.merge(*defaultXmlCopy.get())) | 
|  | fatal("internal manifest tool failed on default xml: " + | 
|  | toString(std::move(e))); | 
|  |  | 
|  | for (StringRef filename : config->manifestInput) { | 
|  | std::unique_ptr<MemoryBuffer> manifest = | 
|  | check(MemoryBuffer::getFile(filename)); | 
|  | // Call takeBuffer to include in /reproduce: output if applicable. | 
|  | if (auto e = merger.merge(driver->takeBuffer(std::move(manifest)))) | 
|  | fatal("internal manifest tool failed on file " + filename + ": " + | 
|  | toString(std::move(e))); | 
|  | } | 
|  |  | 
|  | return std::string(merger.getMergedManifest().get()->getBuffer()); | 
|  | } | 
|  |  | 
|  | static std::string createManifestXmlWithExternalMt(StringRef defaultXml) { | 
|  | // Create the default manifest file as a temporary file. | 
|  | TemporaryFile Default("defaultxml", "manifest"); | 
|  | std::error_code ec; | 
|  | raw_fd_ostream os(Default.path, ec, sys::fs::OF_TextWithCRLF); | 
|  | if (ec) | 
|  | fatal("failed to open " + Default.path + ": " + ec.message()); | 
|  | os << defaultXml; | 
|  | os.close(); | 
|  |  | 
|  | // Merge user-supplied manifests if they are given.  Since libxml2 is not | 
|  | // enabled, we must shell out to Microsoft's mt.exe tool. | 
|  | TemporaryFile user("user", "manifest"); | 
|  |  | 
|  | Executor e("mt.exe"); | 
|  | e.add("/manifest"); | 
|  | e.add(Default.path); | 
|  | for (StringRef filename : config->manifestInput) { | 
|  | e.add("/manifest"); | 
|  | e.add(filename); | 
|  |  | 
|  | // Manually add the file to the /reproduce: tar if needed. | 
|  | if (driver->tar) | 
|  | if (auto mbOrErr = MemoryBuffer::getFile(filename)) | 
|  | driver->takeBuffer(std::move(*mbOrErr)); | 
|  | } | 
|  | e.add("/nologo"); | 
|  | e.add("/out:" + StringRef(user.path)); | 
|  | e.run(); | 
|  |  | 
|  | return std::string( | 
|  | CHECK(MemoryBuffer::getFile(user.path), "could not open " + user.path) | 
|  | .get() | 
|  | ->getBuffer()); | 
|  | } | 
|  |  | 
|  | static std::string createManifestXml() { | 
|  | std::string defaultXml = createDefaultXml(); | 
|  | if (config->manifestInput.empty()) | 
|  | return defaultXml; | 
|  |  | 
|  | if (windows_manifest::isAvailable()) | 
|  | return createManifestXmlWithInternalMt(defaultXml); | 
|  |  | 
|  | return createManifestXmlWithExternalMt(defaultXml); | 
|  | } | 
|  |  | 
|  | static std::unique_ptr<WritableMemoryBuffer> | 
|  | createMemoryBufferForManifestRes(size_t manifestSize) { | 
|  | size_t resSize = alignTo( | 
|  | object::WIN_RES_MAGIC_SIZE + object::WIN_RES_NULL_ENTRY_SIZE + | 
|  | sizeof(object::WinResHeaderPrefix) + sizeof(object::WinResIDs) + | 
|  | sizeof(object::WinResHeaderSuffix) + manifestSize, | 
|  | object::WIN_RES_DATA_ALIGNMENT); | 
|  | return WritableMemoryBuffer::getNewMemBuffer(resSize, config->outputFile + | 
|  | ".manifest.res"); | 
|  | } | 
|  |  | 
|  | static void writeResFileHeader(char *&buf) { | 
|  | memcpy(buf, COFF::WinResMagic, sizeof(COFF::WinResMagic)); | 
|  | buf += sizeof(COFF::WinResMagic); | 
|  | memset(buf, 0, object::WIN_RES_NULL_ENTRY_SIZE); | 
|  | buf += object::WIN_RES_NULL_ENTRY_SIZE; | 
|  | } | 
|  |  | 
|  | static void writeResEntryHeader(char *&buf, size_t manifestSize) { | 
|  | // Write the prefix. | 
|  | auto *prefix = reinterpret_cast<object::WinResHeaderPrefix *>(buf); | 
|  | prefix->DataSize = manifestSize; | 
|  | prefix->HeaderSize = sizeof(object::WinResHeaderPrefix) + | 
|  | sizeof(object::WinResIDs) + | 
|  | sizeof(object::WinResHeaderSuffix); | 
|  | buf += sizeof(object::WinResHeaderPrefix); | 
|  |  | 
|  | // Write the Type/Name IDs. | 
|  | auto *iDs = reinterpret_cast<object::WinResIDs *>(buf); | 
|  | iDs->setType(RT_MANIFEST); | 
|  | iDs->setName(config->manifestID); | 
|  | buf += sizeof(object::WinResIDs); | 
|  |  | 
|  | // Write the suffix. | 
|  | auto *suffix = reinterpret_cast<object::WinResHeaderSuffix *>(buf); | 
|  | suffix->DataVersion = 0; | 
|  | suffix->MemoryFlags = object::WIN_RES_PURE_MOVEABLE; | 
|  | suffix->Language = SUBLANG_ENGLISH_US; | 
|  | suffix->Version = 0; | 
|  | suffix->Characteristics = 0; | 
|  | buf += sizeof(object::WinResHeaderSuffix); | 
|  | } | 
|  |  | 
|  | // Create a resource file containing a manifest XML. | 
|  | std::unique_ptr<MemoryBuffer> createManifestRes() { | 
|  | std::string manifest = createManifestXml(); | 
|  |  | 
|  | std::unique_ptr<WritableMemoryBuffer> res = | 
|  | createMemoryBufferForManifestRes(manifest.size()); | 
|  |  | 
|  | char *buf = res->getBufferStart(); | 
|  | writeResFileHeader(buf); | 
|  | writeResEntryHeader(buf, manifest.size()); | 
|  |  | 
|  | // Copy the manifest data into the .res file. | 
|  | std::copy(manifest.begin(), manifest.end(), buf); | 
|  | return std::move(res); | 
|  | } | 
|  |  | 
|  | void createSideBySideManifest() { | 
|  | std::string path = std::string(config->manifestFile); | 
|  | if (path == "") | 
|  | path = config->outputFile + ".manifest"; | 
|  | std::error_code ec; | 
|  | raw_fd_ostream out(path, ec, sys::fs::OF_TextWithCRLF); | 
|  | if (ec) | 
|  | fatal("failed to create manifest: " + ec.message()); | 
|  | out << createManifestXml(); | 
|  | } | 
|  |  | 
|  | // Parse a string in the form of | 
|  | // "<name>[=<internalname>][,@ordinal[,NONAME]][,DATA][,PRIVATE]" | 
|  | // or "<name>=<dllname>.<name>". | 
|  | // Used for parsing /export arguments. | 
|  | Export parseExport(StringRef arg) { | 
|  | Export e; | 
|  | StringRef rest; | 
|  | std::tie(e.name, rest) = arg.split(","); | 
|  | if (e.name.empty()) | 
|  | goto err; | 
|  |  | 
|  | if (e.name.contains('=')) { | 
|  | StringRef x, y; | 
|  | std::tie(x, y) = e.name.split("="); | 
|  |  | 
|  | // If "<name>=<dllname>.<name>". | 
|  | if (y.contains(".")) { | 
|  | e.name = x; | 
|  | e.forwardTo = y; | 
|  | return e; | 
|  | } | 
|  |  | 
|  | e.extName = x; | 
|  | e.name = y; | 
|  | if (e.name.empty()) | 
|  | goto err; | 
|  | } | 
|  |  | 
|  | // If "<name>=<internalname>[,@ordinal[,NONAME]][,DATA][,PRIVATE]" | 
|  | while (!rest.empty()) { | 
|  | StringRef tok; | 
|  | std::tie(tok, rest) = rest.split(","); | 
|  | if (tok.equals_insensitive("noname")) { | 
|  | if (e.ordinal == 0) | 
|  | goto err; | 
|  | e.noname = true; | 
|  | continue; | 
|  | } | 
|  | if (tok.equals_insensitive("data")) { | 
|  | e.data = true; | 
|  | continue; | 
|  | } | 
|  | if (tok.equals_insensitive("constant")) { | 
|  | e.constant = true; | 
|  | continue; | 
|  | } | 
|  | if (tok.equals_insensitive("private")) { | 
|  | e.isPrivate = true; | 
|  | continue; | 
|  | } | 
|  | if (tok.startswith("@")) { | 
|  | int32_t ord; | 
|  | if (tok.substr(1).getAsInteger(0, ord)) | 
|  | goto err; | 
|  | if (ord <= 0 || 65535 < ord) | 
|  | goto err; | 
|  | e.ordinal = ord; | 
|  | continue; | 
|  | } | 
|  | goto err; | 
|  | } | 
|  | return e; | 
|  |  | 
|  | err: | 
|  | fatal("invalid /export: " + arg); | 
|  | } | 
|  |  | 
|  | static StringRef undecorate(StringRef sym) { | 
|  | if (config->machine != I386) | 
|  | return sym; | 
|  | // In MSVC mode, a fully decorated stdcall function is exported | 
|  | // as-is with the leading underscore (with type IMPORT_NAME). | 
|  | // In MinGW mode, a decorated stdcall function gets the underscore | 
|  | // removed, just like normal cdecl functions. | 
|  | if (sym.startswith("_") && sym.contains('@') && !config->mingw) | 
|  | return sym; | 
|  | return sym.startswith("_") ? sym.substr(1) : sym; | 
|  | } | 
|  |  | 
|  | // Convert stdcall/fastcall style symbols into unsuffixed symbols, | 
|  | // with or without a leading underscore. (MinGW specific.) | 
|  | static StringRef killAt(StringRef sym, bool prefix) { | 
|  | if (sym.empty()) | 
|  | return sym; | 
|  | // Strip any trailing stdcall suffix | 
|  | sym = sym.substr(0, sym.find('@', 1)); | 
|  | if (!sym.startswith("@")) { | 
|  | if (prefix && !sym.startswith("_")) | 
|  | return saver().save("_" + sym); | 
|  | return sym; | 
|  | } | 
|  | // For fastcall, remove the leading @ and replace it with an | 
|  | // underscore, if prefixes are used. | 
|  | sym = sym.substr(1); | 
|  | if (prefix) | 
|  | sym = saver().save("_" + sym); | 
|  | return sym; | 
|  | } | 
|  |  | 
|  | // Performs error checking on all /export arguments. | 
|  | // It also sets ordinals. | 
|  | void fixupExports() { | 
|  | // Symbol ordinals must be unique. | 
|  | std::set<uint16_t> ords; | 
|  | for (Export &e : config->exports) { | 
|  | if (e.ordinal == 0) | 
|  | continue; | 
|  | if (!ords.insert(e.ordinal).second) | 
|  | fatal("duplicate export ordinal: " + e.name); | 
|  | } | 
|  |  | 
|  | for (Export &e : config->exports) { | 
|  | if (!e.forwardTo.empty()) { | 
|  | e.exportName = undecorate(e.name); | 
|  | } else { | 
|  | e.exportName = undecorate(e.extName.empty() ? e.name : e.extName); | 
|  | } | 
|  | } | 
|  |  | 
|  | if (config->killAt && config->machine == I386) { | 
|  | for (Export &e : config->exports) { | 
|  | e.name = killAt(e.name, true); | 
|  | e.exportName = killAt(e.exportName, false); | 
|  | e.extName = killAt(e.extName, true); | 
|  | e.symbolName = killAt(e.symbolName, true); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Uniquefy by name. | 
|  | DenseMap<StringRef, Export *> map(config->exports.size()); | 
|  | std::vector<Export> v; | 
|  | for (Export &e : config->exports) { | 
|  | auto pair = map.insert(std::make_pair(e.exportName, &e)); | 
|  | bool inserted = pair.second; | 
|  | if (inserted) { | 
|  | v.push_back(e); | 
|  | continue; | 
|  | } | 
|  | Export *existing = pair.first->second; | 
|  | if (e == *existing || e.name != existing->name) | 
|  | continue; | 
|  | warn("duplicate /export option: " + e.name); | 
|  | } | 
|  | config->exports = std::move(v); | 
|  |  | 
|  | // Sort by name. | 
|  | std::sort(config->exports.begin(), config->exports.end(), | 
|  | [](const Export &a, const Export &b) { | 
|  | return a.exportName < b.exportName; | 
|  | }); | 
|  | } | 
|  |  | 
|  | void assignExportOrdinals() { | 
|  | // Assign unique ordinals if default (= 0). | 
|  | uint32_t max = 0; | 
|  | for (Export &e : config->exports) | 
|  | max = std::max(max, (uint32_t)e.ordinal); | 
|  | for (Export &e : config->exports) | 
|  | if (e.ordinal == 0) | 
|  | e.ordinal = ++max; | 
|  | if (max > std::numeric_limits<uint16_t>::max()) | 
|  | fatal("too many exported symbols (max " + | 
|  | Twine(std::numeric_limits<uint16_t>::max()) + ")"); | 
|  | } | 
|  |  | 
|  | // Parses a string in the form of "key=value" and check | 
|  | // if value matches previous values for the same key. | 
|  | void checkFailIfMismatch(StringRef arg, InputFile *source) { | 
|  | StringRef k, v; | 
|  | std::tie(k, v) = arg.split('='); | 
|  | if (k.empty() || v.empty()) | 
|  | fatal("/failifmismatch: invalid argument: " + arg); | 
|  | std::pair<StringRef, InputFile *> existing = config->mustMatch[k]; | 
|  | if (!existing.first.empty() && v != existing.first) { | 
|  | std::string sourceStr = source ? toString(source) : "cmd-line"; | 
|  | std::string existingStr = | 
|  | existing.second ? toString(existing.second) : "cmd-line"; | 
|  | fatal("/failifmismatch: mismatch detected for '" + k + "':\n>>> " + | 
|  | existingStr + " has value " + existing.first + "\n>>> " + sourceStr + | 
|  | " has value " + v); | 
|  | } | 
|  | config->mustMatch[k] = {v, source}; | 
|  | } | 
|  |  | 
|  | // Convert Windows resource files (.res files) to a .obj file. | 
|  | // Does what cvtres.exe does, but in-process and cross-platform. | 
|  | MemoryBufferRef convertResToCOFF(ArrayRef<MemoryBufferRef> mbs, | 
|  | ArrayRef<ObjFile *> objs) { | 
|  | object::WindowsResourceParser parser(/* MinGW */ config->mingw); | 
|  |  | 
|  | std::vector<std::string> duplicates; | 
|  | for (MemoryBufferRef mb : mbs) { | 
|  | std::unique_ptr<object::Binary> bin = check(object::createBinary(mb)); | 
|  | object::WindowsResource *rf = dyn_cast<object::WindowsResource>(bin.get()); | 
|  | if (!rf) | 
|  | fatal("cannot compile non-resource file as resource"); | 
|  |  | 
|  | if (auto ec = parser.parse(rf, duplicates)) | 
|  | fatal(toString(std::move(ec))); | 
|  | } | 
|  |  | 
|  | // Note: This processes all .res files before all objs. Ideally they'd be | 
|  | // handled in the same order they were linked (to keep the right one, if | 
|  | // there are duplicates that are tolerated due to forceMultipleRes). | 
|  | for (ObjFile *f : objs) { | 
|  | object::ResourceSectionRef rsf; | 
|  | if (auto ec = rsf.load(f->getCOFFObj())) | 
|  | fatal(toString(f) + ": " + toString(std::move(ec))); | 
|  |  | 
|  | if (auto ec = parser.parse(rsf, f->getName(), duplicates)) | 
|  | fatal(toString(std::move(ec))); | 
|  | } | 
|  |  | 
|  | if (config->mingw) | 
|  | parser.cleanUpManifests(duplicates); | 
|  |  | 
|  | for (const auto &dupeDiag : duplicates) | 
|  | if (config->forceMultipleRes) | 
|  | warn(dupeDiag); | 
|  | else | 
|  | error(dupeDiag); | 
|  |  | 
|  | Expected<std::unique_ptr<MemoryBuffer>> e = | 
|  | llvm::object::writeWindowsResourceCOFF(config->machine, parser, | 
|  | config->timestamp); | 
|  | if (!e) | 
|  | fatal("failed to write .res to COFF: " + toString(e.takeError())); | 
|  |  | 
|  | MemoryBufferRef mbref = **e; | 
|  | make<std::unique_ptr<MemoryBuffer>>(std::move(*e)); // take ownership | 
|  | return mbref; | 
|  | } | 
|  |  | 
|  | // Create OptTable | 
|  |  | 
|  | // Create prefix string literals used in Options.td | 
|  | #define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; | 
|  | #include "Options.inc" | 
|  | #undef PREFIX | 
|  |  | 
|  | // Create table mapping all options defined in Options.td | 
|  | static const llvm::opt::OptTable::Info infoTable[] = { | 
|  | #define OPTION(X1, X2, ID, KIND, GROUP, ALIAS, X7, X8, X9, X10, X11, X12)      \ | 
|  | {X1, X2, X10,         X11,         OPT_##ID, llvm::opt::Option::KIND##Class, \ | 
|  | X9, X8, OPT_##GROUP, OPT_##ALIAS, X7,       X12}, | 
|  | #include "Options.inc" | 
|  | #undef OPTION | 
|  | }; | 
|  |  | 
|  | COFFOptTable::COFFOptTable() : OptTable(infoTable, true) {} | 
|  |  | 
|  | COFFOptTable optTable; | 
|  |  | 
|  | // Set color diagnostics according to --color-diagnostics={auto,always,never} | 
|  | // or --no-color-diagnostics flags. | 
|  | static void handleColorDiagnostics(opt::InputArgList &args) { | 
|  | auto *arg = args.getLastArg(OPT_color_diagnostics, OPT_color_diagnostics_eq, | 
|  | OPT_no_color_diagnostics); | 
|  | if (!arg) | 
|  | return; | 
|  | if (arg->getOption().getID() == OPT_color_diagnostics) { | 
|  | lld::errs().enable_colors(true); | 
|  | } else if (arg->getOption().getID() == OPT_no_color_diagnostics) { | 
|  | lld::errs().enable_colors(false); | 
|  | } else { | 
|  | StringRef s = arg->getValue(); | 
|  | if (s == "always") | 
|  | lld::errs().enable_colors(true); | 
|  | else if (s == "never") | 
|  | lld::errs().enable_colors(false); | 
|  | else if (s != "auto") | 
|  | error("unknown option: --color-diagnostics=" + s); | 
|  | } | 
|  | } | 
|  |  | 
|  | static cl::TokenizerCallback getQuotingStyle(opt::InputArgList &args) { | 
|  | if (auto *arg = args.getLastArg(OPT_rsp_quoting)) { | 
|  | StringRef s = arg->getValue(); | 
|  | if (s != "windows" && s != "posix") | 
|  | error("invalid response file quoting: " + s); | 
|  | if (s == "windows") | 
|  | return cl::TokenizeWindowsCommandLine; | 
|  | return cl::TokenizeGNUCommandLine; | 
|  | } | 
|  | // The COFF linker always defaults to Windows quoting. | 
|  | return cl::TokenizeWindowsCommandLine; | 
|  | } | 
|  |  | 
|  | // Parses a given list of options. | 
|  | opt::InputArgList ArgParser::parse(ArrayRef<const char *> argv) { | 
|  | // Make InputArgList from string vectors. | 
|  | unsigned missingIndex; | 
|  | unsigned missingCount; | 
|  |  | 
|  | // We need to get the quoting style for response files before parsing all | 
|  | // options so we parse here before and ignore all the options but | 
|  | // --rsp-quoting and /lldignoreenv. | 
|  | // (This means --rsp-quoting can't be added through %LINK%.) | 
|  | opt::InputArgList args = optTable.ParseArgs(argv, missingIndex, missingCount); | 
|  |  | 
|  | // Expand response files (arguments in the form of @<filename>) and insert | 
|  | // flags from %LINK% and %_LINK_%, and then parse the argument again. | 
|  | SmallVector<const char *, 256> expandedArgv(argv.data(), | 
|  | argv.data() + argv.size()); | 
|  | if (!args.hasArg(OPT_lldignoreenv)) | 
|  | addLINK(expandedArgv); | 
|  | cl::ExpandResponseFiles(saver(), getQuotingStyle(args), expandedArgv); | 
|  | args = optTable.ParseArgs(makeArrayRef(expandedArgv).drop_front(), | 
|  | missingIndex, missingCount); | 
|  |  | 
|  | // Print the real command line if response files are expanded. | 
|  | if (args.hasArg(OPT_verbose) && argv.size() != expandedArgv.size()) { | 
|  | std::string msg = "Command line:"; | 
|  | for (const char *s : expandedArgv) | 
|  | msg += " " + std::string(s); | 
|  | message(msg); | 
|  | } | 
|  |  | 
|  | // Save the command line after response file expansion so we can write it to | 
|  | // the PDB if necessary. | 
|  | config->argv = {expandedArgv.begin(), expandedArgv.end()}; | 
|  |  | 
|  | // Handle /WX early since it converts missing argument warnings to errors. | 
|  | errorHandler().fatalWarnings = args.hasFlag(OPT_WX, OPT_WX_no, false); | 
|  |  | 
|  | if (missingCount) | 
|  | fatal(Twine(args.getArgString(missingIndex)) + ": missing argument"); | 
|  |  | 
|  | handleColorDiagnostics(args); | 
|  |  | 
|  | for (opt::Arg *arg : args.filtered(OPT_UNKNOWN)) { | 
|  | std::string nearest; | 
|  | if (optTable.findNearest(arg->getAsString(args), nearest) > 1) | 
|  | warn("ignoring unknown argument '" + arg->getAsString(args) + "'"); | 
|  | else | 
|  | warn("ignoring unknown argument '" + arg->getAsString(args) + | 
|  | "', did you mean '" + nearest + "'"); | 
|  | } | 
|  |  | 
|  | if (args.hasArg(OPT_lib)) | 
|  | warn("ignoring /lib since it's not the first argument"); | 
|  |  | 
|  | return args; | 
|  | } | 
|  |  | 
|  | // Tokenizes and parses a given string as command line in .drective section. | 
|  | ParsedDirectives ArgParser::parseDirectives(StringRef s) { | 
|  | ParsedDirectives result; | 
|  | SmallVector<const char *, 16> rest; | 
|  |  | 
|  | // Handle /EXPORT and /INCLUDE in a fast path. These directives can appear for | 
|  | // potentially every symbol in the object, so they must be handled quickly. | 
|  | SmallVector<StringRef, 16> tokens; | 
|  | cl::TokenizeWindowsCommandLineNoCopy(s, saver(), tokens); | 
|  | for (StringRef tok : tokens) { | 
|  | if (tok.startswith_insensitive("/export:") || | 
|  | tok.startswith_insensitive("-export:")) | 
|  | result.exports.push_back(tok.substr(strlen("/export:"))); | 
|  | else if (tok.startswith_insensitive("/include:") || | 
|  | tok.startswith_insensitive("-include:")) | 
|  | result.includes.push_back(tok.substr(strlen("/include:"))); | 
|  | else { | 
|  | // Copy substrings that are not valid C strings. The tokenizer may have | 
|  | // already copied quoted arguments for us, so those do not need to be | 
|  | // copied again. | 
|  | bool HasNul = tok.end() != s.end() && tok.data()[tok.size()] == '\0'; | 
|  | rest.push_back(HasNul ? tok.data() : saver().save(tok).data()); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Make InputArgList from unparsed string vectors. | 
|  | unsigned missingIndex; | 
|  | unsigned missingCount; | 
|  |  | 
|  | result.args = optTable.ParseArgs(rest, missingIndex, missingCount); | 
|  |  | 
|  | if (missingCount) | 
|  | fatal(Twine(result.args.getArgString(missingIndex)) + ": missing argument"); | 
|  | for (auto *arg : result.args.filtered(OPT_UNKNOWN)) | 
|  | warn("ignoring unknown argument: " + arg->getAsString(result.args)); | 
|  | return result; | 
|  | } | 
|  |  | 
|  | // link.exe has an interesting feature. If LINK or _LINK_ environment | 
|  | // variables exist, their contents are handled as command line strings. | 
|  | // So you can pass extra arguments using them. | 
|  | void ArgParser::addLINK(SmallVector<const char *, 256> &argv) { | 
|  | // Concatenate LINK env and command line arguments, and then parse them. | 
|  | if (Optional<std::string> s = Process::GetEnv("LINK")) { | 
|  | std::vector<const char *> v = tokenize(*s); | 
|  | argv.insert(std::next(argv.begin()), v.begin(), v.end()); | 
|  | } | 
|  | if (Optional<std::string> s = Process::GetEnv("_LINK_")) { | 
|  | std::vector<const char *> v = tokenize(*s); | 
|  | argv.insert(std::next(argv.begin()), v.begin(), v.end()); | 
|  | } | 
|  | } | 
|  |  | 
|  | std::vector<const char *> ArgParser::tokenize(StringRef s) { | 
|  | SmallVector<const char *, 16> tokens; | 
|  | cl::TokenizeWindowsCommandLine(s, saver(), tokens); | 
|  | return std::vector<const char *>(tokens.begin(), tokens.end()); | 
|  | } | 
|  |  | 
|  | void printHelp(const char *argv0) { | 
|  | optTable.printHelp(lld::outs(), | 
|  | (std::string(argv0) + " [options] file...").c_str(), | 
|  | "LLVM Linker", false); | 
|  | } | 
|  |  | 
|  | } // namespace coff | 
|  | } // namespace lld |