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